<commit_before>//! This pass checks HIR bodies that may be evaluated at compile-time (e.g., `const`, `static`,
//! `const fn`) for structured control flow (e.g. `if`, `while`), which is forbidden in a const
//! context.
//!
//! By the time the MIR const-checker runs, these high-level constructs have been lowered to
//! control-flow primitives (e.g., `Goto`, `SwitchInt`), making it tough to properly attribute
//! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips
//! through, but errors for structured control flow in a `const` should be emitted here.

use rustc_attr as attr;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_middle::hir::map::Map;
use rustc_middle::ty;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::parse::feature_err;
use rustc_span::{sym, Span, Symbol};

/// An expression that is not *always* legal in a const context.
#[derive(Clone, Copy)]
enum NonConstExpr {
    Loop(hir::LoopSource),
    Match(hir::MatchSource),
}

impl NonConstExpr {
    fn name(self) -> String {
        match self {
            Self::Loop(src) => format!("`{}`", src.name()),
            Self::Match(src) => format!("`{}`", src.name()),
        }
    }

    fn required_feature_gates(self) -> Option<&'static [Symbol]> {
        use hir::LoopSource::*;
        use hir::MatchSource::*;

        let gates: &[_] = match self {
            // A `for` loop's desugaring contains a call to `IntoIterator::into_iter`,
            // so they are not yet allowed.
            // Likewise, `?` desugars to a call to `Try::into_result`.
            Self::Loop(ForLoop) | Self::Match(ForLoopDesugar | TryDesugar | AwaitDesugar) => {
                return None;
            }

            Self::Match(IfLetGuardDesugar) => bug!("`if let` guard outside a `match` expression"),

            // All other expressions are allowed.
            Self::Loop(Loop | While | WhileLet)
            | Self::Match(WhileDesugar | WhileLetDesugar | Normal | IfLetDesugar { .. }) => &[],
        };

        Some(gates)
    }
}

fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
    let mut vis = CheckConstVisitor::new(tcx);
    tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor());
    if tcx.features().enabled(sym::const_trait_impl) {
        tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckConstTraitVisitor::new(tcx));
    }
}

pub(crate) fn provide(providers: &mut Providers) {
    *providers = Providers { check_mod_const_bodies, ..*providers };
}

struct CheckConstTraitVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
}

impl<'tcx> CheckConstTraitVisitor<'tcx> {
    fn new(tcx: TyCtxt<'tcx>) -> Self {
        CheckConstTraitVisitor { tcx }
    }
}

impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor<'tcx> {
    fn visit_item(&mut self, item: &'hir hir::Item<'hir>) {
        let _: Option<_> = try {
            if let hir::ItemKind::Impl(ref imp) = item.kind {
                if let hir::Constness::Const = imp.constness {
                    let did = imp.of_trait.as_ref()?.trait_def_id()?;
                    let trait_fn_cnt = self
                        .tcx
                        .associated_item_def_ids(did)
                        .iter()
                        .filter(|did| {
                            matches!(
                                self.tcx.associated_item(**did),
                                ty::AssocItem { kind: ty::AssocKind::Fn, .. }
                            )
                        })
                        .count();

                    let impl_fn_cnt = imp
                        .items
                        .iter()
                        .filter(|it| matches!(it.kind, hir::AssocItemKind::Fn { .. }))
                        .count();

                    if trait_fn_cnt != impl_fn_cnt {
                        self.tcx
                            .sess
                            .struct_span_err(
                                item.span,
                                "const trait implementations may not use default functions",
                            )
                            .emit();
                    }
                }
            }
        };
    }

    fn visit_trait_item(&mut self, _: &'hir hir::TraitItem<'hir>) {}

    fn visit_impl_item(&mut self, _: &'hir hir::ImplItem<'hir>) {}

    fn visit_foreign_item(&mut self, _: &'hir hir::ForeignItem<'hir>) {}
}

#[derive(Copy, Clone)]
struct CheckConstVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
    const_kind: Option<hir::ConstContext>,
    def_id: Option<LocalDefId>,
}

impl<'tcx> CheckConstVisitor<'tcx> {
    fn new(tcx: TyCtxt<'tcx>) -> Self {
        CheckConstVisitor { tcx, const_kind: None, def_id: None }
    }

    /// Emits an error when an unsupported expression is found in a const context.
    fn const_check_violated(&self, expr: NonConstExpr, span: Span) {
        let Self { tcx, def_id, const_kind } = *self;

        let features = tcx.features();
        let required_gates = expr.required_feature_gates();

        let is_feature_allowed = |feature_gate| {
            // All features require that the corresponding gate be enabled,
            // even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`.
            if !tcx.features().enabled(feature_gate) {
                return false;
            }

            // If `def_id` is `None`, we don't need to consider stability attributes.
            let def_id = match def_id {
                Some(x) => x.to_def_id(),
                None => return true,
            };

            // If this crate is not using stability attributes, or this function is not claiming to be a
            // stable `const fn`, that is all that is required.
            if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
                return true;
            }

            // However, we cannot allow stable `const fn`s to use unstable features without an explicit
            // opt-in via `rustc_allow_const_fn_unstable`.
            attr::rustc_allow_const_fn_unstable(&tcx.sess, &tcx.get_attrs(def_id))
                .any(|name| name == feature_gate)
        };

        match required_gates {
            // Don't emit an error if the user has enabled the requisite feature gates.
            Some(gates) if gates.iter().copied().all(is_feature_allowed) => return,

            // `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
            // corresponding feature gate. This encourages nightly users to use feature gates when
            // possible.
            None if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
                tcx.sess.span_warn(span, "skipping const checks");
                return;
            }

            _ => {}
        }

        let const_kind =
            const_kind.expect("`const_check_violated` may only be called inside a const context");

        let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name());

        let required_gates = required_gates.unwrap_or(&[]);
        let missing_gates: Vec<_> =
            required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect();

        match missing_gates.as_slice() {
            &[] => struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit(),

            &[missing_primary, ref missing_secondary @ ..] => {
                let mut err = feature_err(&tcx.sess.parse_sess, missing_primary, span, &msg);

                // If multiple feature gates would be required to enable this expression, include
                // them as help messages. Don't emit a separate error for each missing feature gate.
                //
                // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
                // is a pretty narrow case, however.
                if tcx.sess.is_nightly_build() {
                    for gate in missing_secondary {
                        let note = format!(
                            "add `#![feature({})]` to the crate attributes to enable",
                            gate,
                        );
                        err.help(&note);
                    }
                }

                err.emit();
            }
        }
    }

    /// Saves the parent `const_kind` before calling `f` and restores it afterwards.
    fn recurse_into(
        &mut self,
        kind: Option<hir::ConstContext>,
        def_id: Option<LocalDefId>,
        f: impl FnOnce(&mut Self),
    ) {
        let parent_def_id = self.def_id;
        let parent_kind = self.const_kind;
        self.def_id = def_id;
        self.const_kind = kind;
        f(self);
        self.def_id = parent_def_id;
        self.const_kind = parent_kind;
    }
}

impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
    type Map = Map<'tcx>;

    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
        NestedVisitorMap::OnlyBodies(self.tcx.hir())
    }

    fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) {
        let kind = Some(hir::ConstContext::Const);
        self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon));
    }

    fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
        let owner = self.tcx.hir().body_owner_def_id(body.id());
        let kind = self.tcx.hir().body_const_context(owner);
        self.recurse_into(kind, Some(owner), |this| intravisit::walk_body(this, body));
    }

    fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
        match &e.kind {
            // Skip the following checks if we are not currently in a const context.
            _ if self.const_kind.is_none() => {}

            hir::ExprKind::Loop(_, _, source, _) => {
                self.const_check_violated(NonConstExpr::Loop(*source), e.span);
            }

            hir::ExprKind::Match(_, _, source) => {
                let non_const_expr = match source {
                    // These are handled by `ExprKind::Loop` above.
                    hir::MatchSource::WhileDesugar
                    | hir::MatchSource::WhileLetDesugar
                    | hir::MatchSource::ForLoopDesugar => None,

                    _ => Some(NonConstExpr::Match(*source)),
                };

                if let Some(expr) = non_const_expr {
                    self.const_check_violated(expr, e.span);
                }
            }

            _ => {}
        }

        intravisit::walk_expr(self, e);
    }
}
<commit_msg>Do the check even when the feature is not enabled<commit_after>//! This pass checks HIR bodies that may be evaluated at compile-time (e.g., `const`, `static`,
//! `const fn`) for structured control flow (e.g. `if`, `while`), which is forbidden in a const
//! context.
//!
//! By the time the MIR const-checker runs, these high-level constructs have been lowered to
//! control-flow primitives (e.g., `Goto`, `SwitchInt`), making it tough to properly attribute
//! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips
//! through, but errors for structured control flow in a `const` should be emitted here.

use rustc_attr as attr;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_middle::hir::map::Map;
use rustc_middle::ty;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::parse::feature_err;
use rustc_span::{sym, Span, Symbol};

/// An expression that is not *always* legal in a const context.
#[derive(Clone, Copy)]
enum NonConstExpr {
    Loop(hir::LoopSource),
    Match(hir::MatchSource),
}

impl NonConstExpr {
    fn name(self) -> String {
        match self {
            Self::Loop(src) => format!("`{}`", src.name()),
            Self::Match(src) => format!("`{}`", src.name()),
        }
    }

    fn required_feature_gates(self) -> Option<&'static [Symbol]> {
        use hir::LoopSource::*;
        use hir::MatchSource::*;

        let gates: &[_] = match self {
            // A `for` loop's desugaring contains a call to `IntoIterator::into_iter`,
            // so they are not yet allowed.
            // Likewise, `?` desugars to a call to `Try::into_result`.
            Self::Loop(ForLoop) | Self::Match(ForLoopDesugar | TryDesugar | AwaitDesugar) => {
                return None;
            }

            Self::Match(IfLetGuardDesugar) => bug!("`if let` guard outside a `match` expression"),

            // All other expressions are allowed.
            Self::Loop(Loop | While | WhileLet)
            | Self::Match(WhileDesugar | WhileLetDesugar | Normal | IfLetDesugar { .. }) => &[],
        };

        Some(gates)
    }
}

fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
    let mut vis = CheckConstVisitor::new(tcx);
    tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor());
    tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckConstTraitVisitor::new(tcx));
}

pub(crate) fn provide(providers: &mut Providers) {
    *providers = Providers { check_mod_const_bodies, ..*providers };
}

struct CheckConstTraitVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
}

impl<'tcx> CheckConstTraitVisitor<'tcx> {
    fn new(tcx: TyCtxt<'tcx>) -> Self {
        CheckConstTraitVisitor { tcx }
    }
}

impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor<'tcx> {
    /// check for const trait impls, and errors if the impl uses provided/default functions
    /// of the trait being implemented; as those provided functions can be non-const.
    fn visit_item(&mut self, item: &'hir hir::Item<'hir>) {
        let _: Option<_> = try {
            if let hir::ItemKind::Impl(ref imp) = item.kind {
                if let hir::Constness::Const = imp.constness {
                    let did = imp.of_trait.as_ref()?.trait_def_id()?;
                    let trait_fn_cnt = self
                        .tcx
                        .associated_item_def_ids(did)
                        .iter()
                        .filter(|did| {
                            matches!(
                                self.tcx.associated_item(**did),
                                ty::AssocItem { kind: ty::AssocKind::Fn, .. }
                            )
                        })
                        .count();

                    let impl_fn_cnt = imp
                        .items
                        .iter()
                        .filter(|it| matches!(it.kind, hir::AssocItemKind::Fn { .. }))
                        .count();

                    // number of trait functions unequal to functions in impl,
                    // meaning that one or more provided/default functions of the
                    // trait are used.
                    if trait_fn_cnt != impl_fn_cnt {
                        self.tcx
                            .sess
                            .struct_span_err(
                                item.span,
                                "const trait implementations may not use default functions",
                            )
                            .emit();
                    }
                }
            }
        };
    }

    fn visit_trait_item(&mut self, _: &'hir hir::TraitItem<'hir>) {}

    fn visit_impl_item(&mut self, _: &'hir hir::ImplItem<'hir>) {}

    fn visit_foreign_item(&mut self, _: &'hir hir::ForeignItem<'hir>) {}
}

#[derive(Copy, Clone)]
struct CheckConstVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
    const_kind: Option<hir::ConstContext>,
    def_id: Option<LocalDefId>,
}

impl<'tcx> CheckConstVisitor<'tcx> {
    fn new(tcx: TyCtxt<'tcx>) -> Self {
        CheckConstVisitor { tcx, const_kind: None, def_id: None }
    }

    /// Emits an error when an unsupported expression is found in a const context.
    fn const_check_violated(&self, expr: NonConstExpr, span: Span) {
        let Self { tcx, def_id, const_kind } = *self;

        let features = tcx.features();
        let required_gates = expr.required_feature_gates();

        let is_feature_allowed = |feature_gate| {
            // All features require that the corresponding gate be enabled,
            // even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`.
            if !tcx.features().enabled(feature_gate) {
                return false;
            }

            // If `def_id` is `None`, we don't need to consider stability attributes.
            let def_id = match def_id {
                Some(x) => x.to_def_id(),
                None => return true,
            };

            // If this crate is not using stability attributes, or this function is not claiming to be a
            // stable `const fn`, that is all that is required.
            if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
                return true;
            }

            // However, we cannot allow stable `const fn`s to use unstable features without an explicit
            // opt-in via `rustc_allow_const_fn_unstable`.
            attr::rustc_allow_const_fn_unstable(&tcx.sess, &tcx.get_attrs(def_id))
                .any(|name| name == feature_gate)
        };

        match required_gates {
            // Don't emit an error if the user has enabled the requisite feature gates.
            Some(gates) if gates.iter().copied().all(is_feature_allowed) => return,

            // `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
            // corresponding feature gate. This encourages nightly users to use feature gates when
            // possible.
            None if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
                tcx.sess.span_warn(span, "skipping const checks");
                return;
            }

            _ => {}
        }

        let const_kind =
            const_kind.expect("`const_check_violated` may only be called inside a const context");

        let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name());

        let required_gates = required_gates.unwrap_or(&[]);
        let missing_gates: Vec<_> =
            required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect();

        match missing_gates.as_slice() {
            &[] => struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit(),

            &[missing_primary, ref missing_secondary @ ..] => {
                let mut err = feature_err(&tcx.sess.parse_sess, missing_primary, span, &msg);

                // If multiple feature gates would be required to enable this expression, include
                // them as help messages. Don't emit a separate error for each missing feature gate.
                //
                // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
                // is a pretty narrow case, however.
                if tcx.sess.is_nightly_build() {
                    for gate in missing_secondary {
                        let note = format!(
                            "add `#![feature({})]` to the crate attributes to enable",
                            gate,
                        );
                        err.help(&note);
                    }
                }

                err.emit();
            }
        }
    }

    /// Saves the parent `const_kind` before calling `f` and restores it afterwards.
    fn recurse_into(
        &mut self,
        kind: Option<hir::ConstContext>,
        def_id: Option<LocalDefId>,
        f: impl FnOnce(&mut Self),
    ) {
        let parent_def_id = self.def_id;
        let parent_kind = self.const_kind;
        self.def_id = def_id;
        self.const_kind = kind;
        f(self);
        self.def_id = parent_def_id;
        self.const_kind = parent_kind;
    }
}

impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
    type Map = Map<'tcx>;

    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
        NestedVisitorMap::OnlyBodies(self.tcx.hir())
    }

    fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) {
        let kind = Some(hir::ConstContext::Const);
        self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon));
    }

    fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
        let owner = self.tcx.hir().body_owner_def_id(body.id());
        let kind = self.tcx.hir().body_const_context(owner);
        self.recurse_into(kind, Some(owner), |this| intravisit::walk_body(this, body));
    }

    fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
        match &e.kind {
            // Skip the following checks if we are not currently in a const context.
            _ if self.const_kind.is_none() => {}

            hir::ExprKind::Loop(_, _, source, _) => {
                self.const_check_violated(NonConstExpr::Loop(*source), e.span);
            }

            hir::ExprKind::Match(_, _, source) => {
                let non_const_expr = match source {
                    // These are handled by `ExprKind::Loop` above.
                    hir::MatchSource::WhileDesugar
                    | hir::MatchSource::WhileLetDesugar
                    | hir::MatchSource::ForLoopDesugar => None,

                    _ => Some(NonConstExpr::Match(*source)),
                };

                if let Some(expr) = non_const_expr {
                    self.const_check_violated(expr, e.span);
                }
            }

            _ => {}
        }

        intravisit::walk_expr(self, e);
    }
}
<|endoftext|>


<commit_before>//! Math module for core vector and matrix operations.

use std::f32::consts;
use std::ops::{Add, Sub};
use rustc_serialize::Decoder;
use rustc_serialize::Decodable;

/// Degrees to radians
pub fn to_radians(x: f32) -> f32 {
    x * consts::PI / 180.0
}

pub trait Clamp {
    fn clamp(self: Self, lower: Self, upper: Self) -> Self;
}

impl Clamp for f32 {
    fn clamp(self, lower: f32, upper: f32) -> f32 {
        self.min(upper).max(lower)
    }
}

/**
 * 3x1 real vector type
 */
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Vec3f {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

impl Vec3f {
    pub fn new(x: f32, y: f32, z: f32) -> Vec3f {
        Vec3f {x: x, y: y, z: z}
    }

    pub fn zero() -> Vec3f {
        Vec3f::new(0.0, 0.0, 0.0)
    }

    pub fn dot(&self, rhs: &Vec3f) -> f32 {
        self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
    }

    pub fn cross(&self, rhs: &Vec3f) -> Vec3f {
        Vec3f {
            x: self.y * rhs.z - self.z * rhs.y,
            y: self.z * rhs.x - self.x * rhs.z,
            z: self.x * rhs.y - self.y * rhs.x,
        }
    }

    pub fn point_mul(&self, rhs: &Vec3f) -> Vec3f {
        Vec3f {
            x: self.x * rhs.x,
            y: self.y * rhs.y,
            z: self.z * rhs.z,
        }
    }

    pub fn scale(&self, rhs: f32) -> Vec3f {
        Vec3f {
            x: self.x * rhs,
            y: self.y * rhs,
            z: self.z * rhs,
        }
    }

    pub fn magnitude_squared(&self) -> f32 {
        self.dot(self)
    }

    pub fn magnitude(&self) -> f32 {
        f32::sqrt(self.magnitude_squared())
    }

    pub fn norm(&self) -> Vec3f {
        self.scale(1.0 / self.magnitude())
    }
}

impl<'a> Add for &'a Vec3f {
    type Output = Vec3f;
    fn add(self, rhs: &'a Vec3f) -> Vec3f {
        Vec3f {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
        }
    }
}

impl<'a> Sub for &'a Vec3f {
    type Output = Vec3f;
    fn sub(self, rhs: &'a Vec3f) -> Vec3f {
        Vec3f {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
        }
    }
}

impl Decodable for Vec3f {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        d.read_seq(|d, len| {
            Ok(Vec3f {
                x: try!(d.read_seq_elt(0, |d| Decodable::decode(d))),
                y: try!(d.read_seq_elt(1, |d| Decodable::decode(d))),
                z: try!(d.read_seq_elt(2, |d| Decodable::decode(d))),
            })
        })
    }
}

/**
 * 4x1 real vector type
 */
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Vec4f {
    pub x: f32,
    pub y: f32,
    pub z: f32,
    pub w: f32,
}

impl Vec4f {
    pub fn new(x: f32, y: f32, z: f32, w: f32) -> Vec4f {
        Vec4f {x: x, y: y, z: z, w: w}
    }

    pub fn zero() -> Vec4f {
        Vec4f::new(0.0, 0.0, 0.0, 0.0)
    }

    pub fn dot(&self, rhs: &Vec4f) -> f32 {
        self.x * rhs.x + self.y * rhs.y + self.z * rhs.z + self.w * rhs.w
    }

    pub fn point_mul(&self, rhs: &Vec4f) -> Vec4f {
        Vec4f {
            x: self.x * rhs.x,
            y: self.y * rhs.y,
            z: self.z * rhs.z,
            w: self.w * rhs.w,
        }
    }

    pub fn scale(&self, rhs: f32) -> Vec4f {
        Vec4f {
            x: self.x * rhs,
            y: self.y * rhs,
            z: self.z * rhs,
            w: self.w * rhs,
        }
    }

    pub fn magnitude_squared(&self) -> f32 {
        self.dot(self)
    }

    pub fn magnitude(&self) -> f32 {
        f32::sqrt(self.magnitude_squared())
    }

    pub fn norm(&self) -> Vec4f {
        self.scale(1.0 / self.magnitude())
    }
}

impl<'a> Add for &'a Vec4f {
    type Output = Vec4f;
    fn add(self, rhs: &'a Vec4f) -> Vec4f {
        Vec4f {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
            w: self.w + rhs.w,
        }
    }
}

impl<'a> Sub for &'a Vec4f {
    type Output = Vec4f;
    fn sub(self, rhs: &'a Vec4f) -> Vec4f {
        Vec4f {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
            w: self.w - rhs.w,
        }
    }
}

impl Decodable for Vec4f {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        d.read_seq(|d, len| {
            Ok(Vec4f {
                x: try!(d.read_seq_elt(0, |d| Decodable::decode(d))),
                y: try!(d.read_seq_elt(1, |d| Decodable::decode(d))),
                z: try!(d.read_seq_elt(2, |d| Decodable::decode(d))),
                w: try!(d.read_seq_elt(3, |d| Decodable::decode(d))),
            })
        })
    }
}

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

    #[test]
    fn test_radians() {
        assert_eq!(to_radians(360.0), consts::PI * 2.0);
    }

    #[test]
    fn test_vec3f() {
        let vec3f = Vec3f::new;
        let u = vec3f(1.0, 2.0, 3.0);
        let v = vec3f(4.0, 5.0, 6.0);
        assert_eq!(&u + &v, vec3f(5.0, 7.0, 9.0));
        assert_eq!(&u - &v, vec3f(-3.0, -3.0, -3.0));
        assert_eq!(Vec3f::zero(), vec3f(0.0, 0.0, 0.0));
        assert_eq!(u.cross(&v), vec3f(-3.0, 6.0, -3.0));
        assert_eq!(u.point_mul(&v), vec3f(4.0, 10.0, 18.0));
        assert_eq!(u.dot(&v), 32.0);
        assert_eq!(u.scale(3.0), vec3f(3.0, 6.0, 9.0));
        assert_eq!(u.magnitude_squared(), 14.0);
        assert_eq!(u.magnitude(), 3.7416575);
        assert_eq!(u.norm(), vec3f(0.26726124, 0.5345225, 0.8017837));
    }

    #[test]
    fn test_vec4f() {
        let vec4f = Vec4f::new;
        let u = vec4f(1.0, 2.0, 3.0, 4.0);
        let v = vec4f(5.0, 6.0, 7.0, 8.0);
        assert_eq!(&u + &v, vec4f(6.0, 8.0, 10.0, 12.0));
        assert_eq!(&u - &v, vec4f(-4.0, -4.0, -4.0, -4.0));
        assert_eq!(Vec4f::zero(), vec4f(0.0, 0.0, 0.0, 0.0));
        assert_eq!(u.point_mul(&v), vec4f(5.0, 12.0, 21.0, 32.0));
        assert_eq!(u.dot(&v), 70.0);
        assert_eq!(u.scale(3.0), vec4f(3.0, 6.0, 9.0, 12.0));
        assert_eq!(u.magnitude_squared(), 30.0);
        assert_eq!(u.magnitude(), 5.477225575);
        assert_eq!(u.norm(), vec4f(0.1825741858, 0.3651483717, 0.5477225575, 0.7302967433));
    }
}
<commit_msg>Add Mat4f<commit_after>//! Math module for core vector and matrix operations.

use std::f32::consts;
use std::ops::{Add, Sub};
use rustc_serialize::Decoder;
use rustc_serialize::Decodable;

/// Degrees to radians
pub fn to_radians(x: f32) -> f32 {
    x * consts::PI / 180.0
}

pub trait Clamp {
    fn clamp(self: Self, lower: Self, upper: Self) -> Self;
}

impl Clamp for f32 {
    fn clamp(self, lower: f32, upper: f32) -> f32 {
        self.min(upper).max(lower)
    }
}

/**
 * 3x1 real vector type
 */
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Vec3f {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

impl Vec3f {
    pub fn new(x: f32, y: f32, z: f32) -> Vec3f {
        Vec3f {x: x, y: y, z: z}
    }

    pub fn zero() -> Vec3f {
        Vec3f::new(0.0, 0.0, 0.0)
    }

    pub fn dot(&self, rhs: &Vec3f) -> f32 {
        self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
    }

    pub fn cross(&self, rhs: &Vec3f) -> Vec3f {
        Vec3f {
            x: self.y * rhs.z - self.z * rhs.y,
            y: self.z * rhs.x - self.x * rhs.z,
            z: self.x * rhs.y - self.y * rhs.x,
        }
    }

    pub fn point_mul(&self, rhs: &Vec3f) -> Vec3f {
        Vec3f {
            x: self.x * rhs.x,
            y: self.y * rhs.y,
            z: self.z * rhs.z,
        }
    }

    pub fn scale(&self, rhs: f32) -> Vec3f {
        Vec3f {
            x: self.x * rhs,
            y: self.y * rhs,
            z: self.z * rhs,
        }
    }

    pub fn magnitude_squared(&self) -> f32 {
        self.dot(self)
    }

    pub fn magnitude(&self) -> f32 {
        f32::sqrt(self.magnitude_squared())
    }

    pub fn norm(&self) -> Vec3f {
        self.scale(1.0 / self.magnitude())
    }
}

impl<'a> Add for &'a Vec3f {
    type Output = Vec3f;
    fn add(self, rhs: &'a Vec3f) -> Vec3f {
        Vec3f {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
        }
    }
}

impl<'a> Sub for &'a Vec3f {
    type Output = Vec3f;
    fn sub(self, rhs: &'a Vec3f) -> Vec3f {
        Vec3f {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
        }
    }
}

impl Decodable for Vec3f {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        d.read_seq(|d, len| {
            Ok(Vec3f {
                x: try!(d.read_seq_elt(0, |d| Decodable::decode(d))),
                y: try!(d.read_seq_elt(1, |d| Decodable::decode(d))),
                z: try!(d.read_seq_elt(2, |d| Decodable::decode(d))),
            })
        })
    }
}

/**
 * 4x1 real vector type
 */
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Vec4f {
    pub x: f32,
    pub y: f32,
    pub z: f32,
    pub w: f32,
}

impl Vec4f {
    pub fn new(x: f32, y: f32, z: f32, w: f32) -> Vec4f {
        Vec4f {x: x, y: y, z: z, w: w}
    }

    pub fn zero() -> Vec4f {
        Vec4f::new(0.0, 0.0, 0.0, 0.0)
    }

    pub fn dot(&self, rhs: &Vec4f) -> f32 {
        self.x * rhs.x + self.y * rhs.y + self.z * rhs.z + self.w * rhs.w
    }

    pub fn point_mul(&self, rhs: &Vec4f) -> Vec4f {
        Vec4f {
            x: self.x * rhs.x,
            y: self.y * rhs.y,
            z: self.z * rhs.z,
            w: self.w * rhs.w,
        }
    }

    pub fn scale(&self, rhs: f32) -> Vec4f {
        Vec4f {
            x: self.x * rhs,
            y: self.y * rhs,
            z: self.z * rhs,
            w: self.w * rhs,
        }
    }

    pub fn magnitude_squared(&self) -> f32 {
        self.dot(self)
    }

    pub fn magnitude(&self) -> f32 {
        f32::sqrt(self.magnitude_squared())
    }

    pub fn norm(&self) -> Vec4f {
        self.scale(1.0 / self.magnitude())
    }
}

impl<'a> Add for &'a Vec4f {
    type Output = Vec4f;
    fn add(self, rhs: &'a Vec4f) -> Vec4f {
        Vec4f {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
            w: self.w + rhs.w,
        }
    }
}

impl<'a> Sub for &'a Vec4f {
    type Output = Vec4f;
    fn sub(self, rhs: &'a Vec4f) -> Vec4f {
        Vec4f {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
            w: self.w - rhs.w,
        }
    }
}

impl Decodable for Vec4f {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        d.read_seq(|d, len| {
            Ok(Vec4f {
                x: try!(d.read_seq_elt(0, |d| Decodable::decode(d))),
                y: try!(d.read_seq_elt(1, |d| Decodable::decode(d))),
                z: try!(d.read_seq_elt(2, |d| Decodable::decode(d))),
                w: try!(d.read_seq_elt(3, |d| Decodable::decode(d))),
            })
        })
    }
}

/**
 * 4x4 Row Matrix
 */
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Mat4f {
    pub r1: Vec4f,
    pub r2: Vec4f,
    pub r3: Vec4f,
    pub r4: Vec4f,
}

impl Mat4f {
    pub fn new(r1: Vec4f, r2: Vec4f, r3: Vec4f, r4: Vec4f) -> Mat4f {
        Mat4f { r1: r1, r2: r2, r3: r3, r4: r4 }
    }

    pub fn mv_multiply(&self, v: &Vec4f) -> Vec4f {
        Vec4f::new(self.r1.dot(v), self.r2.dot(v), self.r3.dot(v), self.r4.dot(v))
    }

    pub fn transpose(&self) -> Mat4f {
        Mat4f::new(
            Vec4f::new(self.r1.x, self.r2.x, self.r3.x, self.r4.x),
            Vec4f::new(self.r1.y, self.r2.y, self.r3.y, self.r4.y),
            Vec4f::new(self.r1.z, self.r2.z, self.r3.z, self.r4.z),
            Vec4f::new(self.r1.w, self.r2.w, self.r3.w, self.r4.w)
        )
    }

    pub fn mm_multiply(&self, rhs: &Mat4f) -> Mat4f {
        let t = rhs.transpose();
        Mat4f::new(
            self.mv_multiply(&t.r1),
            self.mv_multiply(&t.r2),
            self.mv_multiply(&t.r3),
            self.mv_multiply(&t.r4),
        ).transpose()
    }
}

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

    #[test]
    fn test_radians() {
        assert_eq!(to_radians(360.0), consts::PI * 2.0);
    }

    #[test]
    fn test_vec3f() {
        let vec3f = Vec3f::new;
        let u = vec3f(1.0, 2.0, 3.0);
        let v = vec3f(4.0, 5.0, 6.0);
        assert_eq!(&u + &v, vec3f(5.0, 7.0, 9.0));
        assert_eq!(&u - &v, vec3f(-3.0, -3.0, -3.0));
        assert_eq!(Vec3f::zero(), vec3f(0.0, 0.0, 0.0));
        assert_eq!(u.cross(&v), vec3f(-3.0, 6.0, -3.0));
        assert_eq!(u.point_mul(&v), vec3f(4.0, 10.0, 18.0));
        assert_eq!(u.dot(&v), 32.0);
        assert_eq!(u.scale(3.0), vec3f(3.0, 6.0, 9.0));
        assert_eq!(u.magnitude_squared(), 14.0);
        assert_eq!(u.magnitude(), 3.7416575);
        assert_eq!(u.norm(), vec3f(0.26726124, 0.5345225, 0.8017837));
    }

    #[test]
    fn test_vec4f() {
        let vec4f = Vec4f::new;
        let u = vec4f(1.0, 2.0, 3.0, 4.0);
        let v = vec4f(5.0, 6.0, 7.0, 8.0);
        assert_eq!(&u + &v, vec4f(6.0, 8.0, 10.0, 12.0));
        assert_eq!(&u - &v, vec4f(-4.0, -4.0, -4.0, -4.0));
        assert_eq!(Vec4f::zero(), vec4f(0.0, 0.0, 0.0, 0.0));
        assert_eq!(u.point_mul(&v), vec4f(5.0, 12.0, 21.0, 32.0));
        assert_eq!(u.dot(&v), 70.0);
        assert_eq!(u.scale(3.0), vec4f(3.0, 6.0, 9.0, 12.0));
        assert_eq!(u.magnitude_squared(), 30.0);
        assert_eq!(u.magnitude(), 5.477225575);
        assert_eq!(u.norm(), vec4f(0.1825741858, 0.3651483717, 0.5477225575, 0.7302967433));
    }

    #[test]
    fn test_mat4f() {
        let mat4f = Mat4f::new;
        let vec4f = Vec4f::new;
        let A = mat4f(
            vec4f(1.0, 2.0, 3.0, 4.0),
            vec4f(5.0, 6.0, 7.0, 8.0),
            vec4f(9.0, 10.0, 11.0, 12.0),
            vec4f(13.0, 14.0, 15.0, 16.0));
        let B = mat4f(
            vec4f(17.0, 18.0, 19.0, 20.0),
            vec4f(21.0, 22.0, 23.0, 24.0),
            vec4f(25.0, 26.0, 27.0, 28.0),
            vec4f(29.0, 30.0, 31.0, 23.0));
        let C = mat4f(
            vec4f(250.0, 260.0, 270.0, 244.0),
            vec4f(618.0, 644.0, 670.0, 624.0),
            vec4f(986.0, 1028.0, 1070.0, 1004.0),
            vec4f(1354.0, 1412.0, 1470.0, 1384.0));
        assert_eq!(A.mm_multiply(&B), C);
    }
}
<|endoftext|>


<commit_before>#[cfg(test)] use std::ptr;
#[cfg(test)] use std::ffi::CStr;
#[cfg(test)] use std::os::raw::c_char;
#[cfg(test)] use std::io::Cursor;
#[cfg(test)] use llvm::prelude::*;
#[cfg(test)] use llvm::core::*;
#[cfg(test)] use llvm::execution_engine::*;
#[cfg(test)] use compileerror::{ErrorType, err, CompileError, Pos};
#[cfg(test)] use parser::{parse_module, ParseMode};
#[cfg(test)] use codegen::{CodeGenOptions, codegen, cstr};

#[cfg(test)]
fn run(prog: &str, dump_ir: bool) -> Result<u64, CompileError>
{
    let mut cursor = Cursor::new(prog);
    let md = try!(parse_module(&mut cursor, "test", ParseMode::Module));

    let opts = CodeGenOptions{
        dump_ir: dump_ir,
        build_dir: "build".into(),
        program_name: "test".into(),
        runtime_library: "libcobraruntime.a".into(),
        optimize: true,
    };

    let mut ctx = try!(codegen(&md, &opts));

    unsafe {
        LLVMLinkInInterpreter();

        let mut ee: LLVMExecutionEngineRef = ptr::null_mut();
        let mut error_message: *mut c_char = ptr::null_mut();
        if LLVMCreateInterpreterForModule(&mut ee, ctx.take_module_ref(), &mut error_message) != 0 {
            let msg = CStr::from_ptr(error_message).to_str().expect("Invalid C string");
            let e = format!("Unable to create interpreter: {}", msg);
            LLVMDisposeMessage(error_message);
            return err(Pos::zero(), ErrorType::CodegenError(e));
        }

        let mut func: LLVMValueRef = ptr::null_mut();
        if LLVMFindFunction(ee, cstr("main"), &mut func) == 0 {
            let val = LLVMRunFunction(ee, func, 0, ptr::null_mut());
            let result = LLVMGenericValueToInt(val, 0) as u64;
            LLVMDisposeGenericValue(val);
            LLVMDisposeExecutionEngine(ee);
            Ok(result)
        } else {
            LLVMDisposeExecutionEngine(ee);
            err(Pos::zero(), ErrorType::CodegenError("No main function found".into()))
        }
    }
}

#[test]
fn test_number()
{
    assert!(run(r#"
func main() -> int:
    return 5
    ""#, false).unwrap() == 5);
}

#[test]
fn test_call()
{
    assert!(run(r#"
func foo(a: int, b: int) -> int:
    return a + b * 2

func main() -> int:
    return foo(1, 2)
    ""#, false).unwrap() == 5);
}

#[test]
fn test_vars()
{
    assert!(run(r#"
func main() -> int:
    const x = 7
    var y = 6
    y += x
    return y
    ""#, false).unwrap() == 13);
}

#[test]
fn test_global_vars()
{
    assert!(run(r#"
const x = 7
var y = 6
func main() -> int:
    y += x
    return y
    ""#, false).unwrap() == 13);
}

#[test]
fn test_struct()
{
    assert!(run(r#"
struct Point:
    var x = 0, y = 0

    pub func sum(self) -> int:
        return self.x + self.y

func main() -> int:
    var p = Point{4, 9}
    return p.sum()
    ""#, false).unwrap() == 13);
}

#[test]
fn test_post_fix_increment()
{
    assert!(run(r#"
struct Foo:
    pub var z = 6

var bar = Foo{}

func main() -> int:
    var y = 6
    var foo = Foo{}
    foo.z++
    bar.z++
    y++
    return y + foo.z + bar.z
    ""#, false).unwrap() == 21);
}

#[test]
fn test_arrays()
{
    assert!(run(r#"
struct Point:
    var x = 0, y = 0

    pub func sum(self) -> int:
        return self.x + self.y

func main() -> int:
    var p = [Point{4, 9}, Point{3, 4}]
    return p[0].sum() + p[1].sum()
    ""#, false).unwrap() == 20);
}

#[test]
fn test_global_arrays()
{
    let v = run(r#"
struct Point:
    var x = 0, y = 0

    pub func sum(self) -> int:
        return self.x + self.y

var p = [Point{4, 9}, Point{3, 4}]

func main() -> int:
    return p[0].sum() + p[1].sum()
    ""#, false).unwrap();
    assert!(v == 20);
}

/*
#[test]
fn test_struct_with_arrays()
{
    assert!(run(r#"
struct Point:
    var x = 0, y = 0

    pub func sum(self) -> int:
        return self.x + self.y

func main() -> int:
    var p = [Point{4, 9}, Point{3, 4}]
    return p[0].sum() + p[1].sum()
    ""#).unwrap() == 20);
}
*/
<commit_msg>Add test for struct with array member<commit_after>#[cfg(test)] use std::ptr;
#[cfg(test)] use std::ffi::CStr;
#[cfg(test)] use std::os::raw::c_char;
#[cfg(test)] use std::io::Cursor;
#[cfg(test)] use llvm::prelude::*;
#[cfg(test)] use llvm::core::*;
#[cfg(test)] use llvm::execution_engine::*;
#[cfg(test)] use compileerror::{ErrorType, err, CompileError, Pos};
#[cfg(test)] use parser::{parse_module, ParseMode};
#[cfg(test)] use codegen::{CodeGenOptions, codegen, cstr};

#[cfg(test)]
fn run(prog: &str, dump_ir: bool) -> Result<u64, CompileError>
{
    let mut cursor = Cursor::new(prog);
    let md = try!(parse_module(&mut cursor, "test", ParseMode::Module));

    let opts = CodeGenOptions{
        dump_ir: dump_ir,
        build_dir: "build".into(),
        program_name: "test".into(),
        runtime_library: "libcobraruntime.a".into(),
        optimize: true,
    };

    let mut ctx = try!(codegen(&md, &opts));

    unsafe {
        LLVMLinkInInterpreter();

        let mut ee: LLVMExecutionEngineRef = ptr::null_mut();
        let mut error_message: *mut c_char = ptr::null_mut();
        if LLVMCreateInterpreterForModule(&mut ee, ctx.take_module_ref(), &mut error_message) != 0 {
            let msg = CStr::from_ptr(error_message).to_str().expect("Invalid C string");
            let e = format!("Unable to create interpreter: {}", msg);
            LLVMDisposeMessage(error_message);
            return err(Pos::zero(), ErrorType::CodegenError(e));
        }

        let mut func: LLVMValueRef = ptr::null_mut();
        if LLVMFindFunction(ee, cstr("main"), &mut func) == 0 {
            let val = LLVMRunFunction(ee, func, 0, ptr::null_mut());
            let result = LLVMGenericValueToInt(val, 0) as u64;
            LLVMDisposeGenericValue(val);
            LLVMDisposeExecutionEngine(ee);
            Ok(result)
        } else {
            LLVMDisposeExecutionEngine(ee);
            err(Pos::zero(), ErrorType::CodegenError("No main function found".into()))
        }
    }
}

#[test]
fn test_number()
{
    assert!(run(r#"
func main() -> int:
    return 5
    ""#, false).unwrap() == 5);
}

#[test]
fn test_call()
{
    assert!(run(r#"
func foo(a: int, b: int) -> int:
    return a + b * 2

func main() -> int:
    return foo(1, 2)
    ""#, false).unwrap() == 5);
}

#[test]
fn test_vars()
{
    assert!(run(r#"
func main() -> int:
    const x = 7
    var y = 6
    y += x
    return y
    ""#, false).unwrap() == 13);
}

#[test]
fn test_global_vars()
{
    assert!(run(r#"
const x = 7
var y = 6
func main() -> int:
    y += x
    return y
    ""#, false).unwrap() == 13);
}

#[test]
fn test_struct()
{
    assert!(run(r#"
struct Point:
    var x = 0, y = 0

    pub func sum(self) -> int:
        return self.x + self.y

func main() -> int:
    var p = Point{4, 9}
    return p.sum()
    ""#, false).unwrap() == 13);
}

#[test]
fn test_post_fix_increment()
{
    assert!(run(r#"
struct Foo:
    pub var z = 6

var bar = Foo{}

func main() -> int:
    var y = 6
    var foo = Foo{}
    foo.z++
    bar.z++
    y++
    return y + foo.z + bar.z
    ""#, false).unwrap() == 21);
}

#[test]
fn test_arrays()
{
    assert!(run(r#"
struct Point:
    var x = 0, y = 0

    pub func sum(self) -> int:
        return self.x + self.y

func main() -> int:
    var p = [Point{4, 9}, Point{3, 4}]
    return p[0].sum() + p[1].sum()
    ""#, false).unwrap() == 20);
}

#[test]
fn test_global_arrays()
{
    let v = run(r#"
struct Point:
    var x = 0, y = 0

    pub func sum(self) -> int:
        return self.x + self.y

var p = [Point{4, 9}, Point{3, 4}]

func main() -> int:
    return p[0].sum() + p[1].sum()
    ""#, false).unwrap();
    assert!(v == 20);
}

#[test]
fn test_struct_with_arrays()
{
    assert!(run(r#"
struct Point:
    var coords = [0, 0]

    pub func sum(self) -> int:
        return self.coords[0] + self.coords[1]

func main() -> int:
    var p = Point{[4, 10]}
    return p.sum()
    ""#, false).unwrap() == 14);
}
<|endoftext|>


<commit_before>extern crate libc;
use self::libc::*;

use low_level as ll;
use error::Error;
use device::Device;
use context::Context;
use event::{Event, EventStatus, EventLike};
use kernel::Kernel;
use mem::Mem;

use std::ptr;
use std::mem::{size_of, transmute};

pub struct CommandQueue {
    pub id: ll::CommandQueue,
}

pub struct ReadLock<'a, T: Sized + 'a> {
    #[allow(dead_code)]
    sl: &'a [T],
    evt: Event,
}

pub struct WriteLock<'a, T: Sized + 'a> {
    #[allow(dead_code)]
    sl: &'a mut [T],
    evt: Event,
}

impl<'a, T: Sized> Drop for ReadLock<'a, T> {
    fn drop(self: &mut Self) -> () {
        self.evt.wait().ok().expect(
            "error waiting to finish write for ReadLock");
    }
}

impl<'a, T: Sized> EventLike for ReadLock<'a, T> {
    fn status(self: &Self) -> Result<EventStatus, Error> {
        self.evt.status()
    }

    fn wait(self: &Self) -> Result<(), Error> {
        self.evt.wait()
    }
}

impl<'a, T: Sized> Drop for WriteLock<'a, T> {
    fn drop(self: &mut Self) -> () {
        self.evt.wait().ok().expect(
            "error waiting to finish write for ReadLock");
    }
}

impl<'a, T: Sized> EventLike for WriteLock<'a, T> {
    fn status(self: &Self) -> Result<EventStatus, Error> {
        self.evt.status()
    }

    fn wait(self: &Self) -> Result<(), Error> {
        self.evt.wait()
    }
}

impl CommandQueue {
    pub fn new(context: Context,
               device: Device) -> Result<CommandQueue, Error> {
        let props: c_ulong = if try!(device.out_of_order_supported()) {
            1
        } else {
            0
        };
        let id = unsafe {
            let mut err: i32 = 0;
            let id = ll::clCreateCommandQueue(context.id,
                                              device.id,
                                              props,
                                              &mut err);
            try!(Error::check(err));
            id
        };
        Ok(CommandQueue{ id: id })
    }

    fn get_info(self: &Self, param: u32) -> Result<*mut c_void, Error> {
        let mut tr: *mut c_void = ptr::null_mut();
        unsafe {
            try!(Error::check(ll::clGetCommandQueueInfo(self.id,
                                                   param,
                                                   size_of::<*mut c_void>() as size_t,
                                                   transmute(&mut tr),
                                                   ptr::null_mut())));
        }
        Ok(tr)
    }

    pub fn context(self: &Self) -> Result<Context, Error> {
        let val = try!(self.get_info(0x1090));
        let id = unsafe { transmute(val) };
        unsafe {
            try!(Error::check(ll::clRetainContext(id)));
        }
        Ok(Context{id: id})
    }

    pub fn device(self: &Self) -> Result<Device, Error> {
        let val = try!(self.get_info(0x1091));
        let id = unsafe { transmute(val) };
        Ok(Device{id: id})
    }

    fn alloc_ll_buffer(self: &Self,
                    flags: c_ulong,
                    size_bytes: usize) -> Result<ll::Mem, Error> {
        let context = try!(self.context());
        let id = unsafe {
            let mut err: i32 = 0;
            let id = ll::clCreateBuffer(context.id,
                                        flags,
                                        size_bytes as size_t,
                                        ptr::null_mut(),
                                        &mut err);
            try!(Error::check(err));
            id
        };
        Ok(id)
    }

    fn write_buffer_raw<T: Sized>(self: &Self,
                       buf: &mut Mem,
                       offset: usize,
                       size: usize,
                       data: &[T]) -> Result<Event, Error> {
        let mut event_id: ll::Event = ptr::null_mut();
        let host_ptr = &data[0] as *const T;
        let wait_list: Vec<ll::Event> = buf.write_events();
        let wait_size = wait_list.len() as u32;

        let wait_list_ptr: *const ll::Event = match wait_size {
            0 => ptr::null(),
            _ => &wait_list[0],
        };

        unsafe {
            try!(Error::check(ll::clEnqueueWriteBuffer(self.id,
                                                       buf.id,
                                                       0, // don't block
                                                       offset as size_t,
                                                       size as size_t,
                                                       transmute(host_ptr),
                                                       wait_size,
                                                       wait_list_ptr,
                                                       &mut event_id)));
        }

        let event = Event{id: event_id};
        buf.register_write(event.clone());
        Ok(event)
    }

    fn read_buffer_raw<T: Sized>(self: &Self,
                                 buf: &Mem,
                                 offset: usize,
                                 size: usize,
                                 data: &mut [T]) -> Result<Event, Error> {
        let mut event_id: ll::Event = ptr::null_mut();
        let host_ptr = &mut data[0] as *mut T;
        let wait_list: Vec<ll::Event> = buf.read_events();
        let wait_size = wait_list.len() as u32;

        let wait_list_ptr: *const ll::Event = match wait_size {
            0 => ptr::null(),
            _ => &wait_list[0],
        };

        unsafe {
            try!(Error::check(ll::clEnqueueReadBuffer(self.id,
                                                      buf.id,
                                                      0, // don't block,
                                                      offset as size_t,
                                                      size as size_t,
                                                      transmute(host_ptr),
                                                      wait_size,
                                                      wait_list_ptr,
                                                      &mut event_id)));
        }

        let event = Event{id: event_id};
        buf.register_read(event.clone());
        Ok(event)
    }

    pub fn create_buffer(self: &Self,
                               size_bytes: usize) -> Result<Mem, Error> {
        let id = try!(self.alloc_ll_buffer(0, size_bytes));
        let mut tr = Mem::new(id);
        // one-byte write to place buffer on device and ensure
        // allocated memory is okay
        let _ = try!(self.write_buffer_raw(&mut tr,
                                           0,
                                           1,
                                           &[self]));
        Ok(tr)
    }

    pub fn write_buffer<'a, 'b, T: Sized + 'a>(self: &Self,
                                  mem: &'b mut Mem,
                                  slice: &'a [T]) -> Result<ReadLock<'a, T>, Error> {
        let size_bytes = size_of::<T>()*slice.len();
        let evt = try!(self.write_buffer_raw(mem,
                                             0,
                                             size_bytes,
                                             slice));
        Ok(ReadLock{ evt: evt, sl: slice })
    }

    pub fn read_buffer<'a, T: Sized>(self: &Self,
                                 mem: &Mem,
                                 slice: &'a mut [T]) -> Result<WriteLock<'a, T>, Error> {
        let size_bytes = size_of::<T>()*slice.len();
        let evt = try!(self.read_buffer_raw(mem,
                                            0,
                                            size_bytes,
                                            slice));
        Ok(WriteLock{ evt: evt, sl: slice })
    }

    fn next_mul(self: &Self, global: usize, local: usize) -> usize {
        if global % local == 0 {
            global
        } else {
            ((global/local)+1)*local
        }
    }

    pub fn run( self: &Self,
                kernel: &mut Kernel,
                local_size: (usize, usize, usize),
                global_size: (usize, usize, usize)) -> Result<Event, Error> {
        let events = kernel.run_events();
        let global_size_fixed = (self.next_mul(global_size.0, local_size.0),
                                self.next_mul(global_size.1, local_size.1),
                                self.next_mul(global_size.2, local_size.2));

        let local_vec = vec!(local_size.0 as size_t,
                             local_size.1 as size_t,
                             local_size.2 as size_t);
        let global_vec = vec!(global_size_fixed.0 as size_t,
                              global_size_fixed.1 as size_t,
                              global_size_fixed.2 as size_t);

        let mut event_id: ll::Event = ptr::null_mut();
        unsafe {
            try!(Error::check(ll::clEnqueueNDRangeKernel(self.id,
                                                         kernel.id,
                                                         3,
                                                         ptr::null(),
                                                         &global_vec[0],
                                                         &local_vec[0],
                                                         events.len() as u32,
                                                         &events[0],
                                                         &mut event_id)));
        }
        let event = Event{id: event_id};

        try!(kernel.register_run(event.clone()));
        Ok(event)
    }
}

impl Drop for CommandQueue {
    fn drop(self: &mut Self) -> () {
        unsafe {
            Error::check(ll::clReleaseCommandQueue(self.id))
                .ok().expect("Error releasing command queue");
        }
    }
}

impl Clone for CommandQueue {
    fn clone(self: &Self) -> Self {
        unsafe {
            Error::check(ll::clRetainCommandQueue(self.id))
                .ok().expect("Error retaining command queue");
        }
        CommandQueue{ id: self.id }
    }
}

<commit_msg>removes buffer creation safety check<commit_after>extern crate libc;
use self::libc::*;

use low_level as ll;
use error::Error;
use device::Device;
use context::Context;
use event::{Event, EventStatus, EventLike};
use kernel::Kernel;
use mem::Mem;

use std::ptr;
use std::mem::{size_of, transmute};

pub struct CommandQueue {
    pub id: ll::CommandQueue,
}

pub struct ReadLock<'a, T: Sized + 'a> {
    #[allow(dead_code)]
    sl: &'a [T],
    evt: Event,
}

pub struct WriteLock<'a, T: Sized + 'a> {
    #[allow(dead_code)]
    sl: &'a mut [T],
    evt: Event,
}

impl<'a, T: Sized> Drop for ReadLock<'a, T> {
    fn drop(self: &mut Self) -> () {
        self.evt.wait().ok().expect(
            "error waiting to finish write for ReadLock");
    }
}

impl<'a, T: Sized> EventLike for ReadLock<'a, T> {
    fn status(self: &Self) -> Result<EventStatus, Error> {
        self.evt.status()
    }

    fn wait(self: &Self) -> Result<(), Error> {
        self.evt.wait()
    }
}

impl<'a, T: Sized> Drop for WriteLock<'a, T> {
    fn drop(self: &mut Self) -> () {
        self.evt.wait().ok().expect(
            "error waiting to finish write for ReadLock");
    }
}

impl<'a, T: Sized> EventLike for WriteLock<'a, T> {
    fn status(self: &Self) -> Result<EventStatus, Error> {
        self.evt.status()
    }

    fn wait(self: &Self) -> Result<(), Error> {
        self.evt.wait()
    }
}

impl CommandQueue {
    pub fn new(context: Context,
               device: Device) -> Result<CommandQueue, Error> {
        let props: c_ulong = if try!(device.out_of_order_supported()) {
            1
        } else {
            0
        };
        let id = unsafe {
            let mut err: i32 = 0;
            let id = ll::clCreateCommandQueue(context.id,
                                              device.id,
                                              props,
                                              &mut err);
            try!(Error::check(err));
            id
        };
        Ok(CommandQueue{ id: id })
    }

    fn get_info(self: &Self, param: u32) -> Result<*mut c_void, Error> {
        let mut tr: *mut c_void = ptr::null_mut();
        unsafe {
            try!(Error::check(ll::clGetCommandQueueInfo(self.id,
                                                   param,
                                                   size_of::<*mut c_void>() as size_t,
                                                   transmute(&mut tr),
                                                   ptr::null_mut())));
        }
        Ok(tr)
    }

    pub fn context(self: &Self) -> Result<Context, Error> {
        let val = try!(self.get_info(0x1090));
        let id = unsafe { transmute(val) };
        unsafe {
            try!(Error::check(ll::clRetainContext(id)));
        }
        Ok(Context{id: id})
    }

    pub fn device(self: &Self) -> Result<Device, Error> {
        let val = try!(self.get_info(0x1091));
        let id = unsafe { transmute(val) };
        Ok(Device{id: id})
    }

    fn alloc_ll_buffer(self: &Self,
                    flags: c_ulong,
                    size_bytes: usize) -> Result<ll::Mem, Error> {
        let context = try!(self.context());
        let id = unsafe {
            let mut err: i32 = 0;
            let id = ll::clCreateBuffer(context.id,
                                        flags,
                                        size_bytes as size_t,
                                        ptr::null_mut(),
                                        &mut err);
            try!(Error::check(err));
            id
        };
        Ok(id)
    }

    fn write_buffer_raw<T: Sized>(self: &Self,
                       buf: &mut Mem,
                       offset: usize,
                       size: usize,
                       data: &[T]) -> Result<Event, Error> {
        let mut event_id: ll::Event = ptr::null_mut();
        let host_ptr = &data[0] as *const T;
        let wait_list: Vec<ll::Event> = buf.write_events();
        let wait_size = wait_list.len() as u32;

        let wait_list_ptr: *const ll::Event = match wait_size {
            0 => ptr::null(),
            _ => &wait_list[0],
        };

        unsafe {
            try!(Error::check(ll::clEnqueueWriteBuffer(self.id,
                                                       buf.id,
                                                       0, // don't block
                                                       offset as size_t,
                                                       size as size_t,
                                                       transmute(host_ptr),
                                                       wait_size,
                                                       wait_list_ptr,
                                                       &mut event_id)));
        }

        let event = Event{id: event_id};
        buf.register_write(event.clone());
        Ok(event)
    }

    fn read_buffer_raw<T: Sized>(self: &Self,
                                 buf: &Mem,
                                 offset: usize,
                                 size: usize,
                                 data: &mut [T]) -> Result<Event, Error> {
        let mut event_id: ll::Event = ptr::null_mut();
        let host_ptr = &mut data[0] as *mut T;
        let wait_list: Vec<ll::Event> = buf.read_events();
        let wait_size = wait_list.len() as u32;

        let wait_list_ptr: *const ll::Event = match wait_size {
            0 => ptr::null(),
            _ => &wait_list[0],
        };

        unsafe {
            try!(Error::check(ll::clEnqueueReadBuffer(self.id,
                                                      buf.id,
                                                      0, // don't block,
                                                      offset as size_t,
                                                      size as size_t,
                                                      transmute(host_ptr),
                                                      wait_size,
                                                      wait_list_ptr,
                                                      &mut event_id)));
        }

        let event = Event{id: event_id};
        buf.register_read(event.clone());
        Ok(event)
    }

    pub fn create_buffer(self: &Self,
                               size_bytes: usize) -> Result<Mem, Error> {
        let id = try!(self.alloc_ll_buffer(0, size_bytes));
        let mut tr = Mem::new(id);

        // one-byte write to place buffer on device and ensure
        // allocated memory is okay
        /*
        let _ = try!(self.write_buffer_raw(&mut tr,
                                           0,
                                           1,
                                           &[self]));
        */
        Ok(tr)
    }

    pub fn write_buffer<'a, 'b, T: Sized + 'a>(self: &Self,
                                  mem: &'b mut Mem,
                                  slice: &'a [T]) -> Result<ReadLock<'a, T>, Error> {
        let size_bytes = size_of::<T>()*slice.len();
        let evt = try!(self.write_buffer_raw(mem,
                                             0,
                                             size_bytes,
                                             slice));
        Ok(ReadLock{ evt: evt, sl: slice })
    }

    pub fn read_buffer<'a, T: Sized>(self: &Self,
                                 mem: &Mem,
                                 slice: &'a mut [T]) -> Result<WriteLock<'a, T>, Error> {
        let size_bytes = size_of::<T>()*slice.len();
        let evt = try!(self.read_buffer_raw(mem,
                                            0,
                                            size_bytes,
                                            slice));
        Ok(WriteLock{ evt: evt, sl: slice })
    }

    fn next_mul(self: &Self, global: usize, local: usize) -> usize {
        if global % local == 0 {
            global
        } else {
            ((global/local)+1)*local
        }
    }

    pub fn run( self: &Self,
                kernel: &mut Kernel,
                local_size: (usize, usize, usize),
                global_size: (usize, usize, usize)) -> Result<Event, Error> {
        let events = kernel.run_events();
        let global_size_fixed = (self.next_mul(global_size.0, local_size.0),
                                self.next_mul(global_size.1, local_size.1),
                                self.next_mul(global_size.2, local_size.2));

        let local_vec = vec!(local_size.0 as size_t,
                             local_size.1 as size_t,
                             local_size.2 as size_t);
        let global_vec = vec!(global_size_fixed.0 as size_t,
                              global_size_fixed.1 as size_t,
                              global_size_fixed.2 as size_t);

        let mut event_id: ll::Event = ptr::null_mut();
        unsafe {
            try!(Error::check(ll::clEnqueueNDRangeKernel(self.id,
                                                         kernel.id,
                                                         3,
                                                         ptr::null(),
                                                         &global_vec[0],
                                                         &local_vec[0],
                                                         events.len() as u32,
                                                         &events[0],
                                                         &mut event_id)));
        }
        let event = Event{id: event_id};

        try!(kernel.register_run(event.clone()));
        Ok(event)
    }
}

impl Drop for CommandQueue {
    fn drop(self: &mut Self) -> () {
        unsafe {
            Error::check(ll::clReleaseCommandQueue(self.id))
                .ok().expect("Error releasing command queue");
        }
    }
}

impl Clone for CommandQueue {
    fn clone(self: &Self) -> Self {
        unsafe {
            Error::check(ll::clRetainCommandQueue(self.id))
                .ok().expect("Error retaining command queue");
        }
        CommandQueue{ id: self.id }
    }
}

<|endoftext|>


<commit_before>//! Tests with reference images.
//!
//! This is the bulk of librsvg's black-box tests.  In principle, each test takes an SVG file, renders
//! it to a raster image, and compares that image to a reference image stored on disk.  If the images
//! are "too different", the test fails.  We allow for minor differences in rendering to account for
//! antialiasing artifacts, floating-point variations, and such.
//!

#![cfg(test)]
use crate::test_compare_render_output;
use test_generator::test_resources;

use cairo;
use librsvg::{
    surface_utils::shared_surface::{SharedImageSurface, SurfaceType},
    CairoRenderer, IntrinsicDimensions, Length, Loader,
};
use std::path::PathBuf;

use crate::reference_utils::{Compare, Evaluate, Reference};
use crate::utils::{load_svg, render_document, setup_font_map, setup_language, SurfaceSize};

// The original reference images from the SVG1.1 test suite are at 72 DPI.
const TEST_SUITE_DPI: f64 = 72.0;

// https://gitlab.gnome.org/GNOME/librsvg/issues/91
//
// We were computing some offsets incorrectly if the initial transformation matrix
// passed to rsvg_handle_render_cairo() was not the identity matrix.  So,
// we create a surface with a "frame" around the destination for the image,
// and then only consider the pixels inside the frame.  This will require us
// to have a non-identity transformation (i.e. a translation matrix), which
// will test for this bug.
//
// The frame size is meant to be a ridiculous number to simulate an arbitrary
// offset.
const FRAME_SIZE: i32 = 47;

fn reference_test(path: &str) {
    setup_language();
    setup_font_map();

    let path = PathBuf::from(path);
    let path_base_name = path.file_stem().unwrap().to_string_lossy().to_owned();
    if path_base_name.starts_with("ignore") {
        return;
    }

    let reference = reference_path(&path);

    let handle = Loader::new()
        .read_path(&path)
        .unwrap_or_else(|e| panic!("could not load: {}", e));

    let renderer = CairoRenderer::new(&handle)
        .test_mode()
        .with_dpi(TEST_SUITE_DPI, TEST_SUITE_DPI);
    let (width, height) = image_size(renderer.intrinsic_dimensions(), TEST_SUITE_DPI);

    let surface = cairo::ImageSurface::create(
        cairo::Format::ARgb32,
        width + 2 * FRAME_SIZE,
        height + 2 * FRAME_SIZE,
    )
    .unwrap();

    {
        let cr = cairo::Context::new(&surface).expect("Failed to create a cairo context");
        cr.translate(f64::from(FRAME_SIZE), f64::from(FRAME_SIZE));
        renderer
            .render_document(
                &cr,
                &cairo::Rectangle {
                    x: 0.0,
                    y: 0.0,
                    width: f64::from(width),
                    height: f64::from(height),
                },
            )
            .unwrap();
    }

    let surface = extract_rectangle(&surface, FRAME_SIZE, FRAME_SIZE, width, height).unwrap();

    let output_surf = SharedImageSurface::wrap(surface, SurfaceType::SRgb).unwrap();

    Reference::from_png(&reference)
        .compare(&output_surf)
        .evaluate(&output_surf, &path_base_name);
}

/// Turns `/foo/bar/baz.svg` into `/foo/bar/baz-ref.svg`.
fn reference_path(path: &PathBuf) -> PathBuf {
    let basename = path.file_stem().unwrap();

    let mut reference_filename = basename.to_string_lossy().into_owned();
    reference_filename.push_str("-ref.png");

    path.with_file_name(reference_filename)
}

fn extract_rectangle(
    source: &cairo::ImageSurface,
    x: i32,
    y: i32,
    w: i32,
    h: i32,
) -> Result<cairo::ImageSurface, cairo::Error> {
    let dest = cairo::ImageSurface::create(cairo::Format::ARgb32, w, h)?;
    let cr = cairo::Context::new(&dest).expect("Failed to create a cairo context");
    cr.set_source_surface(&source, f64::from(-x), f64::from(-y))
        .unwrap();
    cr.paint().unwrap();
    Ok(dest)
}

/// Computes the (width, height) pixel size at which an SVG should be rendered, based on its intrinsic dimensions.
///
/// # Panics:
///
/// Will panic if none of the following conditions are met:
///
/// * Width and height both exist
/// * Width and height do not exist, but viewBox exists.
fn image_size(dim: IntrinsicDimensions, dpi: f64) -> (i32, i32) {
    let IntrinsicDimensions {
        width,
        height,
        vbox,
    } = dim;

    use librsvg::LengthUnit::*;

    if let (Some(width), Some(height)) = (width, height) {
        if !(has_supported_unit(&width) && has_supported_unit(&height)) {
            panic!("SVG has unsupported unit type in width or height");
        }
    }

    #[rustfmt::skip]
    let (width, height) = match (width, height, vbox) {
        (Some(Length { length: w, unit: Percent }),
         Some(Length { length: h, unit: Percent }), vbox) if w == 1.0 && h == 1.0 => {
            if let Some(vbox) = vbox {
                (vbox.width, vbox.height)
            } else {
                panic!("SVG with percentage width/height must have a viewBox");
            }
        }

        (Some(Length { length: _, unit: Percent }),
         Some(Length { length: _, unit: Percent }), _) => {
            panic!("Test suite only supports percentage width/height at 100%");
        }

        (Some(w), Some(h), _) => {
            (normalize(&w, dpi), normalize(&h, dpi))
        }

        (None, None, Some(vbox)) => (vbox.width, vbox.height),

        (_, _, _) => panic!("Test suite does not support the dimensions of this file"),
    };

    // Keep in sync with c_api.rs
    let width = checked_i32(width.round());
    let height = checked_i32(height.round());

    (width, height)
}

// Keep in sync with c_api.rs
fn checked_i32(x: f64) -> i32 {
    cast::i32(x).expect("overflow when converting f64 to i32")
}

fn has_supported_unit(l: &Length) -> bool {
    use librsvg::LengthUnit::*;

    match l.unit {
        Percent | Px | In | Cm | Mm | Pt | Pc => true,
        _ => false,
    }
}

const POINTS_PER_INCH: f64 = 72.0;
const CM_PER_INCH: f64 = 2.54;
const MM_PER_INCH: f64 = 25.4;
const PICA_PER_INCH: f64 = 6.0;

fn normalize(l: &Length, dpi: f64) -> f64 {
    use librsvg::LengthUnit::*;

    match l.unit {
        Px => l.length,
        In => l.length * dpi,
        Cm => l.length * dpi / CM_PER_INCH,
        Mm => l.length * dpi / MM_PER_INCH,
        Pt => l.length * dpi / POINTS_PER_INCH,
        Pc => l.length * dpi / PICA_PER_INCH,
        _ => panic!("unsupported length unit"),
    }
}

#[test_resources("tests/fixtures/reftests/*.svg")]
fn reftests(name: &str) {
    reference_test(name);
}

#[test_resources("tests/fixtures/reftests/adwaita/*.svg")]
fn adwaita(name: &str) {
    reference_test(name);
}

#[test_resources("tests/fixtures/reftests/bugs/*.svg")]
fn bugs(name: &str) {
    reference_test(name);
}

#[test_resources("tests/fixtures/reftests/svg1.1/*.svg")]
fn svg_1_1(name: &str) {
    reference_test(name);
}

#[test_resources("tests/fixtures/reftests/svg2/*.svg")]
fn svg_2(name: &str) {
    reference_test(name);
}

test_compare_render_output!(
    marker_orient_auto_start_reverse,
    100,
    100,
    br##"<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
    <defs>
      <marker id="marker" orient="auto-start-reverse" viewBox="0 0 10 10"
              refX="0" refY="5" markerWidth="10" markerHeight="10"
              markerUnits="userSpaceOnUse">
        <path d="M0,0 L10,5 L0,10 Z" fill="green"/>
      </marker>
    </defs>

    <path d="M20,50 L80,50" marker-start="url(#marker)" marker-end="url(#marker)" stroke-width="10" stroke="black"/>
  </svg>"##,
    br##"<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
      <path
         d="M 20,55 10,50 20,45 Z"
         id="triangle1" fill="green"/>
      <path
         d="m 80,45 10,5 -10,5 z"
         id="triangle2" fill="green"/>
      <rect
         id="rectangle"
         width="60"
         height="10"
         x="20"
         y="45" fill="black"/>
    </svg>"##,
  );
<commit_msg>Add test for context-stroke in markers<commit_after>//! Tests with reference images.
//!
//! This is the bulk of librsvg's black-box tests.  In principle, each test takes an SVG file, renders
//! it to a raster image, and compares that image to a reference image stored on disk.  If the images
//! are "too different", the test fails.  We allow for minor differences in rendering to account for
//! antialiasing artifacts, floating-point variations, and such.
//!

#![cfg(test)]
use crate::test_compare_render_output;
use test_generator::test_resources;

use cairo;
use librsvg::{
    surface_utils::shared_surface::{SharedImageSurface, SurfaceType},
    CairoRenderer, IntrinsicDimensions, Length, Loader,
};
use std::path::PathBuf;

use crate::reference_utils::{Compare, Evaluate, Reference};
use crate::utils::{load_svg, render_document, setup_font_map, setup_language, SurfaceSize};

// The original reference images from the SVG1.1 test suite are at 72 DPI.
const TEST_SUITE_DPI: f64 = 72.0;

// https://gitlab.gnome.org/GNOME/librsvg/issues/91
//
// We were computing some offsets incorrectly if the initial transformation matrix
// passed to rsvg_handle_render_cairo() was not the identity matrix.  So,
// we create a surface with a "frame" around the destination for the image,
// and then only consider the pixels inside the frame.  This will require us
// to have a non-identity transformation (i.e. a translation matrix), which
// will test for this bug.
//
// The frame size is meant to be a ridiculous number to simulate an arbitrary
// offset.
const FRAME_SIZE: i32 = 47;

fn reference_test(path: &str) {
    setup_language();
    setup_font_map();

    let path = PathBuf::from(path);
    let path_base_name = path.file_stem().unwrap().to_string_lossy().to_owned();
    if path_base_name.starts_with("ignore") {
        return;
    }

    let reference = reference_path(&path);

    let handle = Loader::new()
        .read_path(&path)
        .unwrap_or_else(|e| panic!("could not load: {}", e));

    let renderer = CairoRenderer::new(&handle)
        .test_mode()
        .with_dpi(TEST_SUITE_DPI, TEST_SUITE_DPI);
    let (width, height) = image_size(renderer.intrinsic_dimensions(), TEST_SUITE_DPI);

    let surface = cairo::ImageSurface::create(
        cairo::Format::ARgb32,
        width + 2 * FRAME_SIZE,
        height + 2 * FRAME_SIZE,
    )
    .unwrap();

    {
        let cr = cairo::Context::new(&surface).expect("Failed to create a cairo context");
        cr.translate(f64::from(FRAME_SIZE), f64::from(FRAME_SIZE));
        renderer
            .render_document(
                &cr,
                &cairo::Rectangle {
                    x: 0.0,
                    y: 0.0,
                    width: f64::from(width),
                    height: f64::from(height),
                },
            )
            .unwrap();
    }

    let surface = extract_rectangle(&surface, FRAME_SIZE, FRAME_SIZE, width, height).unwrap();

    let output_surf = SharedImageSurface::wrap(surface, SurfaceType::SRgb).unwrap();

    Reference::from_png(&reference)
        .compare(&output_surf)
        .evaluate(&output_surf, &path_base_name);
}

/// Turns `/foo/bar/baz.svg` into `/foo/bar/baz-ref.svg`.
fn reference_path(path: &PathBuf) -> PathBuf {
    let basename = path.file_stem().unwrap();

    let mut reference_filename = basename.to_string_lossy().into_owned();
    reference_filename.push_str("-ref.png");

    path.with_file_name(reference_filename)
}

fn extract_rectangle(
    source: &cairo::ImageSurface,
    x: i32,
    y: i32,
    w: i32,
    h: i32,
) -> Result<cairo::ImageSurface, cairo::Error> {
    let dest = cairo::ImageSurface::create(cairo::Format::ARgb32, w, h)?;
    let cr = cairo::Context::new(&dest).expect("Failed to create a cairo context");
    cr.set_source_surface(&source, f64::from(-x), f64::from(-y))
        .unwrap();
    cr.paint().unwrap();
    Ok(dest)
}

/// Computes the (width, height) pixel size at which an SVG should be rendered, based on its intrinsic dimensions.
///
/// # Panics:
///
/// Will panic if none of the following conditions are met:
///
/// * Width and height both exist
/// * Width and height do not exist, but viewBox exists.
fn image_size(dim: IntrinsicDimensions, dpi: f64) -> (i32, i32) {
    let IntrinsicDimensions {
        width,
        height,
        vbox,
    } = dim;

    use librsvg::LengthUnit::*;

    if let (Some(width), Some(height)) = (width, height) {
        if !(has_supported_unit(&width) && has_supported_unit(&height)) {
            panic!("SVG has unsupported unit type in width or height");
        }
    }

    #[rustfmt::skip]
    let (width, height) = match (width, height, vbox) {
        (Some(Length { length: w, unit: Percent }),
         Some(Length { length: h, unit: Percent }), vbox) if w == 1.0 && h == 1.0 => {
            if let Some(vbox) = vbox {
                (vbox.width, vbox.height)
            } else {
                panic!("SVG with percentage width/height must have a viewBox");
            }
        }

        (Some(Length { length: _, unit: Percent }),
         Some(Length { length: _, unit: Percent }), _) => {
            panic!("Test suite only supports percentage width/height at 100%");
        }

        (Some(w), Some(h), _) => {
            (normalize(&w, dpi), normalize(&h, dpi))
        }

        (None, None, Some(vbox)) => (vbox.width, vbox.height),

        (_, _, _) => panic!("Test suite does not support the dimensions of this file"),
    };

    // Keep in sync with c_api.rs
    let width = checked_i32(width.round());
    let height = checked_i32(height.round());

    (width, height)
}

// Keep in sync with c_api.rs
fn checked_i32(x: f64) -> i32 {
    cast::i32(x).expect("overflow when converting f64 to i32")
}

fn has_supported_unit(l: &Length) -> bool {
    use librsvg::LengthUnit::*;

    match l.unit {
        Percent | Px | In | Cm | Mm | Pt | Pc => true,
        _ => false,
    }
}

const POINTS_PER_INCH: f64 = 72.0;
const CM_PER_INCH: f64 = 2.54;
const MM_PER_INCH: f64 = 25.4;
const PICA_PER_INCH: f64 = 6.0;

fn normalize(l: &Length, dpi: f64) -> f64 {
    use librsvg::LengthUnit::*;

    match l.unit {
        Px => l.length,
        In => l.length * dpi,
        Cm => l.length * dpi / CM_PER_INCH,
        Mm => l.length * dpi / MM_PER_INCH,
        Pt => l.length * dpi / POINTS_PER_INCH,
        Pc => l.length * dpi / PICA_PER_INCH,
        _ => panic!("unsupported length unit"),
    }
}

#[test_resources("tests/fixtures/reftests/*.svg")]
fn reftests(name: &str) {
    reference_test(name);
}

#[test_resources("tests/fixtures/reftests/adwaita/*.svg")]
fn adwaita(name: &str) {
    reference_test(name);
}

#[test_resources("tests/fixtures/reftests/bugs/*.svg")]
fn bugs(name: &str) {
    reference_test(name);
}

#[test_resources("tests/fixtures/reftests/svg1.1/*.svg")]
fn svg_1_1(name: &str) {
    reference_test(name);
}

#[test_resources("tests/fixtures/reftests/svg2/*.svg")]
fn svg_2(name: &str) {
    reference_test(name);
}

test_compare_render_output!(
    marker_orient_auto_start_reverse,
    100,
    100,
    br##"<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
    <defs>
      <marker id="marker" orient="auto-start-reverse" viewBox="0 0 10 10"
              refX="0" refY="5" markerWidth="10" markerHeight="10"
              markerUnits="userSpaceOnUse">
        <path d="M0,0 L10,5 L0,10 Z" fill="green"/>
      </marker>
    </defs>

    <path d="M20,50 L80,50" marker-start="url(#marker)" marker-end="url(#marker)" stroke-width="10" stroke="black"/>
  </svg>"##,

    br##"<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
      <path
         d="M 20,55 10,50 20,45 Z"
         id="triangle1" fill="green"/>
      <path
         d="m 80,45 10,5 -10,5 z"
         id="triangle2" fill="green"/>
      <rect
         id="rectangle"
         width="60"
         height="10"
         x="20"
         y="45" fill="black"/>
    </svg>"##,
);

test_compare_render_output!(
    marker_context_stroke,
    400,
    400,
    br##"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400">
  <style>
    .path1 {
      fill: none;
      stroke-width: 4px;
      marker: url(#marker1);
    }
  </style>

  <path class="path1" d="M20,20 L200,20 L380,20" stroke="lime"/>

  <marker id="marker1" markerWidth="12" markerHeight="12" refX="6" refY="6"
          markerUnits="userSpaceOnUse">
    <circle cx="6" cy="6" r="3"
            fill="white" stroke="context-stroke" stroke-width="2"/>
  </marker>
</svg>"##,
    br##"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400">
  <path d="M20,20 L200,20 L380,20" stroke="lime" stroke-width="4"/>
  <circle cx="20" cy="20" r="3" stroke-width="2" fill="white" stroke="lime"/>
  <circle cx="200" cy="20" r="3" stroke-width="2" fill="white" stroke="lime"/>
  <circle cx="380" cy="20" r="3" stroke-width="2" fill="white" stroke="lime"/>
</svg>"##,
);
<|endoftext|>


<commit_before>#![crate_name = "uutest"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) mahkoh (ju.orth [at] gmail [dot] com)
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate libc;
extern crate num;

use std::os::{args_as_bytes};
use std::str::{from_utf8};
use num::bigint::{BigInt};

pub fn uumain(_: Vec<String>) -> int {
    let args = args_as_bytes();
    let args: Vec<&[u8]> = args.iter().map(|a| a.as_slice()).collect();
    let args = args.as_slice();
    if args.len() == 0 {
        return 2;
    }
    let args = match args[0] {
        b"[" => match args[args.len()-1] {
            b"]" => args.slice(1, args.len()-1),
            _ => return 2,
        },
        _ => args.slice(1, args.len()),
    };
    let rv = match args.len() {
        0 => false,
        1 => one(args),
        2 => two(args),
        3 => three(args),
        4 => four(args),
        _ => return 2,
    };
    1 - rv as int
}

fn one(args: &[&[u8]]) -> bool {
    args[0].len() > 0
}

fn two(args: &[&[u8]]) -> bool {
    match args[0] {
        b"!" => !one(args.slice_from(1)),
        b"-b" => path(args[1], BlockSpecial),
        b"-c" => path(args[1], CharacterSpecial),
        b"-d" => path(args[1], Directory),
        b"-e" => path(args[1], Exists),
        b"-f" => path(args[1], Regular),
        b"-g" => path(args[1], GroupIDFlag),
        b"-h" => path(args[1], SymLink),
        b"-L" => path(args[1], SymLink),
        b"-n" => one(args.slice_from(1)),
        b"-p" => path(args[1], FIFO),
        b"-r" => path(args[1], Readable),
        b"-S" => path(args[1], Socket),
        b"-s" => path(args[1], NonEmpty),
        b"-t" => isatty(args[1]),
        b"-u" => path(args[1], UserIDFlag),
        b"-w" => path(args[1], Writable),
        b"-x" => path(args[1], Executable),
        b"-z" => !one(args.slice_from(1)),
        _ => false,
    }
}

fn three(args: &[&[u8]]) -> bool {
    match args[1] {
        b"=" => args[0] == args[2],
        b"!=" => args[0] != args[2],
        b"-eq" => integers(args[0], args[2], Equal),
        b"-ne" => integers(args[0], args[2], Unequal),
        b"-gt" => integers(args[0], args[2], Greater),
        b"-ge" => integers(args[0], args[2], GreaterEqual),
        b"-lt" => integers(args[0], args[2], Less),
        b"-le" => integers(args[0], args[2], LessEqual),
        _ => match args[0] {
            b"!" => !two(args.slice_from(1)),
            _ => false,
        }
    }
}

fn four(args: &[&[u8]]) -> bool {
    match args[0] {
        b"!" => !three(args.slice_from(1)),
        _ => false,
    }
}

enum IntegerCondition {
    Equal,
    Unequal,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
}

fn integers(a: &[u8], b: &[u8], cond: IntegerCondition) -> bool {
    let (a, b): (&str, &str) = match (from_utf8(a), from_utf8(b)) {
        (Some(a), Some(b)) => (a, b),
        _ => return false,
    };
    let (a, b): (BigInt, BigInt) = match (from_str(a), from_str(b)) {
        (Some(a), Some(b)) => (a, b),
        _ => return false,
    };
    match cond {
        Equal        => a == b,
        Unequal      => a != b,
        Greater      => a >  b,
        GreaterEqual => a >= b,
        Less         => a <  b,
        LessEqual    => a <= b,
    }
}

fn isatty(fd: &[u8]) -> bool {
    use libc::{isatty};
    from_utf8(fd).and_then(|s| from_str(s))
            .map(|i| unsafe { isatty(i) == 1 }).unwrap_or(false)
}

#[deriving(Eq, PartialEq)]
enum PathCondition {
    BlockSpecial,
    CharacterSpecial,
    Directory,
    Exists,
    Regular,
    GroupIDFlag,
    SymLink,
    FIFO,
    Readable,
    Socket,
    NonEmpty,
    UserIDFlag,
    Writable,
    Executable,
}

#[cfg(not(windows))]
fn path(path: &[u8], cond: PathCondition) -> bool {
    use libc::{stat, lstat, S_IFMT, S_IFLNK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFREG};
    use libc::{S_IFIFO, mode_t};
    static S_ISUID: mode_t = 0o4000;
    static S_ISGID: mode_t = 0o2000;
    static S_IFSOCK: mode_t = 0o140000;

    enum Permission {
        Read    = 0o4,
        Write   = 0o2,
        Execute = 0o1,
    }
    let perm = |stat: stat, p: Permission| {
        use libc::{getgid, getuid};
        let (uid, gid) = unsafe { (getuid(), getgid()) };
        if uid == stat.st_uid {
            stat.st_mode & (p as mode_t << 6) != 0
        } else if gid == stat.st_gid {
            stat.st_mode & (p as mode_t << 3) != 0
        } else {
            stat.st_mode & (p as mode_t << 0) != 0
        }
    };

    let path = unsafe { path.to_c_str_unchecked() };
    let mut stat = unsafe { std::mem::zeroed() };
    if cond == SymLink {
        if unsafe { lstat(path.as_ptr(), &mut stat) } == 0 {
            if stat.st_mode & S_IFMT == S_IFLNK {
                return true;
            }
        }
        return false;
    }
    if unsafe { libc::stat(path.as_ptr(), &mut stat) } != 0 {
        return false;
    }
    let file_type = stat.st_mode & S_IFMT;
    match cond {
        BlockSpecial     => file_type == S_IFBLK,
        CharacterSpecial => file_type == S_IFCHR,
        Directory        => file_type == S_IFDIR,
        Exists           => true,
        Regular          => file_type == S_IFREG,
        GroupIDFlag      => stat.st_mode & S_ISGID != 0,
        SymLink          => true,
        FIFO             => file_type == S_IFIFO,
        Readable         => perm(stat, Read),
        Socket           => file_type == S_IFSOCK,
        NonEmpty         => stat.st_size > 0,
        UserIDFlag       => stat.st_mode & S_ISUID != 0,
        Writable         => perm(stat, Write),
        Executable       => perm(stat, Execute),
    }
}

#[cfg(windows)]
fn path(path: &[u8], cond: PathCondition) -> bool {
    use std::io::{TypeFile, TypeDirectory, TypeBlockSpecial, TypeNamedPipe};
    use std::io::fs::{stat};
    use std::path::{Path};

    let path = match Path::new_opt(path) {
        Some(p) => p,
        None => return false,
    };
    let stat = match stat(&path) {
        Ok(s) => s,
        _ => return false,
    };
    match cond {
        BlockSpecial     => stat.kind == TypeBlockSpecial,
        CharacterSpecial => false,
        Directory        => stat.kind == TypeDirectory,
        Exists           => true,
        Regular          => stat.kind == TypeFile,
        GroupIDFlag      => false,
        SymLink          => false,
        FIFO             => stat.kind == TypeNamedPipe,
        Readable         => false, // TODO
        Socket           => false, // TODO?
        NonEmpty         => stat.size >  0,
        UserIDFlag       => false,
        Writable         => false, // TODO
        Executable       => false, // TODO
    }
}

<commit_msg>Allow test to be checked by the Busybox test suite<commit_after>#![crate_name = "uutest"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) mahkoh (ju.orth [at] gmail [dot] com)
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate libc;
extern crate num;

use std::os::{args_as_bytes};
use std::str::{from_utf8};
use num::bigint::{BigInt};

static NAME: &'static str = "test";

// TODO: decide how to handle non-UTF8 input for all the utils
pub fn uumain(_: Vec<String>) -> int {
    let args = args_as_bytes();
    let args: Vec<&[u8]> = args.iter().map(|a| a.as_slice()).collect();
    if args.len() == 0 {
        return 2;
    }
    let args =
        if !args[0].ends_with(NAME.as_bytes()) {
            args.slice_from(1)
        } else {
            args.as_slice()
        };
    let args = match args[0] {
        b"[" => match args[args.len() - 1] {
            b"]" => args.slice(1, args.len() - 1),
            _ => return 2,
        },
        _ => args.slice(1, args.len()),
    };
    let rv = match args.len() {
        0 => false,
        1 => one(args),
        2 => two(args),
        3 => three(args),
        4 => four(args),
        _ => return 2,
    };
    1 - rv as int
}

fn one(args: &[&[u8]]) -> bool {
    args[0].len() > 0
}

fn two(args: &[&[u8]]) -> bool {
    match args[0] {
        b"!" => !one(args.slice_from(1)),
        b"-b" => path(args[1], BlockSpecial),
        b"-c" => path(args[1], CharacterSpecial),
        b"-d" => path(args[1], Directory),
        b"-e" => path(args[1], Exists),
        b"-f" => path(args[1], Regular),
        b"-g" => path(args[1], GroupIDFlag),
        b"-h" => path(args[1], SymLink),
        b"-L" => path(args[1], SymLink),
        b"-n" => one(args.slice_from(1)),
        b"-p" => path(args[1], FIFO),
        b"-r" => path(args[1], Readable),
        b"-S" => path(args[1], Socket),
        b"-s" => path(args[1], NonEmpty),
        b"-t" => isatty(args[1]),
        b"-u" => path(args[1], UserIDFlag),
        b"-w" => path(args[1], Writable),
        b"-x" => path(args[1], Executable),
        b"-z" => !one(args.slice_from(1)),
        _ => false,
    }
}

fn three(args: &[&[u8]]) -> bool {
    match args[1] {
        b"=" => args[0] == args[2],
        b"!=" => args[0] != args[2],
        b"-eq" => integers(args[0], args[2], Equal),
        b"-ne" => integers(args[0], args[2], Unequal),
        b"-gt" => integers(args[0], args[2], Greater),
        b"-ge" => integers(args[0], args[2], GreaterEqual),
        b"-lt" => integers(args[0], args[2], Less),
        b"-le" => integers(args[0], args[2], LessEqual),
        _ => match args[0] {
            b"!" => !two(args.slice_from(1)),
            _ => false,
        }
    }
}

fn four(args: &[&[u8]]) -> bool {
    match args[0] {
        b"!" => !three(args.slice_from(1)),
        _ => false,
    }
}

enum IntegerCondition {
    Equal,
    Unequal,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
}

fn integers(a: &[u8], b: &[u8], cond: IntegerCondition) -> bool {
    let (a, b): (&str, &str) = match (from_utf8(a), from_utf8(b)) {
        (Some(a), Some(b)) => (a, b),
        _ => return false,
    };
    let (a, b): (BigInt, BigInt) = match (from_str(a), from_str(b)) {
        (Some(a), Some(b)) => (a, b),
        _ => return false,
    };
    match cond {
        Equal        => a == b,
        Unequal      => a != b,
        Greater      => a >  b,
        GreaterEqual => a >= b,
        Less         => a <  b,
        LessEqual    => a <= b,
    }
}

fn isatty(fd: &[u8]) -> bool {
    use libc::{isatty};
    from_utf8(fd).and_then(|s| from_str(s))
            .map(|i| unsafe { isatty(i) == 1 }).unwrap_or(false)
}

#[deriving(Eq, PartialEq)]
enum PathCondition {
    BlockSpecial,
    CharacterSpecial,
    Directory,
    Exists,
    Regular,
    GroupIDFlag,
    SymLink,
    FIFO,
    Readable,
    Socket,
    NonEmpty,
    UserIDFlag,
    Writable,
    Executable,
}

#[cfg(not(windows))]
fn path(path: &[u8], cond: PathCondition) -> bool {
    use libc::{stat, lstat, S_IFMT, S_IFLNK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFREG};
    use libc::{S_IFIFO, mode_t};
    static S_ISUID: mode_t = 0o4000;
    static S_ISGID: mode_t = 0o2000;
    static S_IFSOCK: mode_t = 0o140000;

    enum Permission {
        Read    = 0o4,
        Write   = 0o2,
        Execute = 0o1,
    }
    let perm = |stat: stat, p: Permission| {
        use libc::{getgid, getuid};
        let (uid, gid) = unsafe { (getuid(), getgid()) };
        if uid == stat.st_uid {
            stat.st_mode & (p as mode_t << 6) != 0
        } else if gid == stat.st_gid {
            stat.st_mode & (p as mode_t << 3) != 0
        } else {
            stat.st_mode & (p as mode_t << 0) != 0
        }
    };

    let path = unsafe { path.to_c_str_unchecked() };
    let mut stat = unsafe { std::mem::zeroed() };
    if cond == SymLink {
        if unsafe { lstat(path.as_ptr(), &mut stat) } == 0 {
            if stat.st_mode & S_IFMT == S_IFLNK {
                return true;
            }
        }
        return false;
    }
    if unsafe { libc::stat(path.as_ptr(), &mut stat) } != 0 {
        return false;
    }
    let file_type = stat.st_mode & S_IFMT;
    match cond {
        BlockSpecial     => file_type == S_IFBLK,
        CharacterSpecial => file_type == S_IFCHR,
        Directory        => file_type == S_IFDIR,
        Exists           => true,
        Regular          => file_type == S_IFREG,
        GroupIDFlag      => stat.st_mode & S_ISGID != 0,
        SymLink          => true,
        FIFO             => file_type == S_IFIFO,
        Readable         => perm(stat, Read),
        Socket           => file_type == S_IFSOCK,
        NonEmpty         => stat.st_size > 0,
        UserIDFlag       => stat.st_mode & S_ISUID != 0,
        Writable         => perm(stat, Write),
        Executable       => perm(stat, Execute),
    }
}

#[cfg(windows)]
fn path(path: &[u8], cond: PathCondition) -> bool {
    use std::io::{TypeFile, TypeDirectory, TypeBlockSpecial, TypeNamedPipe};
    use std::io::fs::{stat};
    use std::path::{Path};

    let path = match Path::new_opt(path) {
        Some(p) => p,
        None => return false,
    };
    let stat = match stat(&path) {
        Ok(s) => s,
        _ => return false,
    };
    match cond {
        BlockSpecial     => stat.kind == TypeBlockSpecial,
        CharacterSpecial => false,
        Directory        => stat.kind == TypeDirectory,
        Exists           => true,
        Regular          => stat.kind == TypeFile,
        GroupIDFlag      => false,
        SymLink          => false,
        FIFO             => stat.kind == TypeNamedPipe,
        Readable         => false, // TODO
        Socket           => false, // TODO?
        NonEmpty         => stat.size > 0,
        UserIDFlag       => false,
        Writable         => false, // TODO
        Executable       => false, // TODO
    }
}

<|endoftext|>