<commit_before>use std::cell::{Cell, RefCell};

use cairo::{self, ImageSurface};
use rulinalg::matrix::{BaseMatrix, Matrix};

use attributes::Attribute;
use drawing_ctx::DrawingCtx;
use error::NodeError;
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, RsvgCNodeImpl, RsvgNode};
use parsers::{self, ListLength, NumberListError, ParseError};
use property_bag::PropertyBag;
use surface_utils::{
    iterators::{PixelRectangle, Pixels},
    shared_surface::SharedImageSurface,
    EdgeMode,
    ImageSurfaceDataExt,
    Pixel,
};
use util::clamp;

use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
use super::{make_result, Filter, FilterError, PrimitiveWithInput};

/// The `feConvolveMatrix` filter primitive.
pub struct ConvolveMatrix {
    base: PrimitiveWithInput,
    order: Cell<(u32, u32)>,
    kernel_matrix: RefCell<Option<Matrix<f64>>>,
    divisor: Cell<Option<f64>>,
    bias: Cell<f64>,
    target_x: Cell<Option<u32>>,
    target_y: Cell<Option<u32>>,
    edge_mode: Cell<EdgeMode>,
    kernel_unit_length: Cell<Option<(f64, f64)>>,
    preserve_alpha: Cell<bool>,
}

impl ConvolveMatrix {
    /// Constructs a new `ConvolveMatrix` with empty properties.
    #[inline]
    pub fn new() -> ConvolveMatrix {
        ConvolveMatrix {
            base: PrimitiveWithInput::new::<Self>(),
            order: Cell::new((3, 3)),
            kernel_matrix: RefCell::new(None),
            divisor: Cell::new(None),
            bias: Cell::new(0.0),
            target_x: Cell::new(None),
            target_y: Cell::new(None),
            edge_mode: Cell::new(EdgeMode::Duplicate),
            kernel_unit_length: Cell::new(None),
            preserve_alpha: Cell::new(false),
        }
    }
}

impl NodeTrait for ConvolveMatrix {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)?;

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::Order => self.order.set(
                    parsers::integer_optional_integer(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|(x, y)| {
                            if x > 0 && y > 0 {
                                Ok((x as u32, y as u32))
                            } else {
                                Err(NodeError::value_error(
                                    attr,
                                    "values must be greater than 0",
                                ))
                            }
                        })?,
                ),
                Attribute::Divisor => self.divisor.set(Some(
                    parsers::number(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|x| {
                            if x != 0.0 {
                                Ok(x)
                            } else {
                                Err(NodeError::value_error(attr, "divisor cannot be equal to 0"))
                            }
                        })?,
                )),
                Attribute::Bias => self
                    .bias
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                Attribute::EdgeMode => self.edge_mode.set(EdgeMode::parse(attr, value)?),
                Attribute::KernelUnitLength => self.kernel_unit_length.set(Some(
                    parsers::number_optional_number(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|(x, y)| {
                            if x > 0.0 && y > 0.0 {
                                Ok((x, y))
                            } else {
                                Err(NodeError::value_error(
                                    attr,
                                    "kernelUnitLength can't be less or equal to zero",
                                ))
                            }
                        })?,
                )),
                Attribute::PreserveAlpha => self.preserve_alpha.set(match value {
                    "false" => false,
                    "true" => true,
                    _ => {
                        return Err(NodeError::parse_error(
                            attr,
                            ParseError::new("expected false or true"),
                        ))
                    }
                }),
                _ => (),
            }
        }

        // target_x and target_y depend on order.
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::TargetX => self.target_x.set(Some(
                    parsers::integer(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|x| {
                            if x >= 0 && x < self.order.get().0 as i32 {
                                Ok(x as u32)
                            } else {
                                Err(NodeError::value_error(
                                    attr,
                                    "targetX must be greater or equal to zero and less than orderX",
                                ))
                            }
                        })?,
                )),
                Attribute::TargetY => self.target_y.set(Some(
                    parsers::integer(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|x| {
                            if x >= 0 && x < self.order.get().1 as i32 {
                                Ok(x as u32)
                            } else {
                                Err(NodeError::value_error(
                                    attr,
                                    "targetY must be greater or equal to zero and less than orderY",
                                ))
                            }
                        })?,
                )),
                _ => (),
            }
        }

        // Default values for target_x and target_y.
        if self.target_x.get().is_none() {
            self.target_x.set(Some(self.order.get().0 / 2));
        }
        if self.target_y.get().is_none() {
            self.target_y.set(Some(self.order.get().1 / 2));
        }

        // Finally, parse the kernel matrix.
        for (_, attr, value) in pbag
            .iter()
            .filter(|(_, attr, _)| *attr == Attribute::KernelMatrix)
        {
            self.kernel_matrix.replace(Some({
                let number_of_elements = self.order.get().0 as usize * self.order.get().1 as usize;

                Matrix::new(
                    self.order.get().1 as usize,
                    self.order.get().0 as usize,
                    parsers::number_list_from_str(value, ListLength::Exact(number_of_elements))
                        .map_err(|err| {
                            NodeError::parse_error(
                                attr,
                                match err {
                                    NumberListError::IncorrectNumberOfElements => {
                                        ParseError::new(format!(
                                            "incorrect number of elements: expected {}",
                                            number_of_elements
                                        ))
                                    }
                                    NumberListError::Parse(err) => err,
                                },
                            )
                        })?,
                )
            }));
        }

        // Default value for the divisor.
        if self.divisor.get().is_none() {
            self.divisor
                .set(Some(self.kernel_matrix.borrow().as_ref().unwrap().sum()));

            if self.divisor.get().unwrap() == 0.0 {
                self.divisor.set(Some(1.0));
            }
        }

        Ok(())
    }

    #[inline]
    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        self.base.get_c_impl()
    }
}

impl Filter for ConvolveMatrix {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = make_result(self.base.get_input(ctx, draw_ctx))?;
        let mut bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .into_irect(draw_ctx);
        let original_bounds = bounds;

        let mut input_surface = if self.preserve_alpha.get() {
            // preserve_alpha means we need to premultiply and unpremultiply the values.
            //
            // HACK: this is storing unpremultiplied pixels in an ARGB32 image surface (which is
            // supposed to be premultiplied pixels).
            let mut unpremultiplied_surface = ImageSurface::create(
                cairo::Format::ARgb32,
                input.surface().width(),
                input.surface().height(),
            ).map_err(FilterError::OutputSurfaceCreation)?;

            let stride = unpremultiplied_surface.get_stride() as usize;
            {
                let mut data = unpremultiplied_surface.get_data().unwrap();

                for (x, y, pixel) in Pixels::new(input.surface(), bounds) {
                    let new_pixel = if pixel.a == 0 {
                        pixel
                    } else {
                        let alpha = f64::from(pixel.a) / 255.0;
                        let unpremultiply = |x| (f64::from(x) / alpha).round() as u8;

                        Pixel {
                            r: unpremultiply(pixel.r),
                            g: unpremultiply(pixel.g),
                            b: unpremultiply(pixel.b),
                            a: pixel.a,
                        }
                    };

                    data.set_pixel(stride, new_pixel, x, y);
                }
            }

            SharedImageSurface::new(unpremultiplied_surface).unwrap()
        } else {
            input.surface().clone()
        };

        if let Some((dx, dy)) = self.kernel_unit_length.get() {
            // Scale the input surface to match kernel_unit_length.
            let paffine = ctx.paffine();
            let ox = paffine.xx * dx + paffine.xy * dy;
            let oy = paffine.yx * dx + paffine.yy * dy;

            let ox = 1.0 / ox;
            let oy = 1.0 / oy;

            bounds = IRect {
                x0: (f64::from(bounds.x0) * ox).floor() as i32,
                y0: (f64::from(bounds.y0) * oy).floor() as i32,
                x1: (f64::from(bounds.x1) * ox).ceil() as i32,
                y1: (f64::from(bounds.y1) * oy).ceil() as i32,
            };

            let surface = ImageSurface::create(
                cairo::Format::ARgb32,
                (f64::from(input_surface.width()) * ox).ceil() as i32,
                (f64::from(input_surface.height()) * oy).ceil() as i32,
            ).map_err(FilterError::OutputSurfaceCreation)?;

            {
                let cr = cairo::Context::new(&surface);
                cr.rectangle(
                    bounds.x0 as f64,
                    bounds.y0 as f64,
                    (bounds.x1 - bounds.x0) as f64,
                    (bounds.y1 - bounds.y0) as f64,
                );
                cr.clip();

                cr.scale(ox, oy);
                input_surface.set_as_source_surface(&cr, 0.0, 0.0);
                cr.paint();
            }

            input_surface = SharedImageSurface::new(surface).unwrap();
        }

        let matrix = self.kernel_matrix.borrow();
        let matrix = matrix.as_ref().unwrap();

        let mut output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            input_surface.width(),
            input_surface.height(),
        ).map_err(FilterError::OutputSurfaceCreation)?;

        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            for (x, y, pixel) in Pixels::new(&input_surface, bounds) {
                // Compute the convolution rectangle bounds.
                let kernel_bounds = IRect {
                    x0: x as i32 - self.target_x.get().unwrap() as i32,
                    y0: y as i32 - self.target_y.get().unwrap() as i32,
                    x1: x as i32 - self.target_x.get().unwrap() as i32 + self.order.get().0 as i32,
                    y1: y as i32 - self.target_y.get().unwrap() as i32 + self.order.get().1 as i32,
                };

                // Do the convolution.
                let mut r = 0.0;
                let mut g = 0.0;
                let mut b = 0.0;
                let mut a = 0.0;

                for (x, y, pixel) in
                    PixelRectangle::new(&input_surface, bounds, kernel_bounds, self.edge_mode.get())
                {
                    let kernel_x = (kernel_bounds.x1 - x - 1) as usize;
                    let kernel_y = (kernel_bounds.y1 - y - 1) as usize;

                    r += f64::from(pixel.r) / 255.0 * matrix[[kernel_y, kernel_x]];
                    g += f64::from(pixel.g) / 255.0 * matrix[[kernel_y, kernel_x]];
                    b += f64::from(pixel.b) / 255.0 * matrix[[kernel_y, kernel_x]];

                    if !self.preserve_alpha.get() {
                        a += f64::from(pixel.a) / 255.0 * matrix[[kernel_y, kernel_x]];
                    }
                }

                // If preserve_alpha is true, set a to the source alpha value.
                if self.preserve_alpha.get() {
                    a = f64::from(pixel.a) / 255.0;
                } else {
                    a = a / self.divisor.get().unwrap() + self.bias.get();
                }

                let clamped_a = clamp(a, 0.0, 1.0);

                let compute = |x| {
                    let x = x / self.divisor.get().unwrap() + self.bias.get() * a;

                    let x = if self.preserve_alpha.get() {
                        // Premultiply the output value.
                        clamp(x, 0.0, 1.0) * clamped_a
                    } else {
                        clamp(x, 0.0, clamped_a)
                    };

                    (x * 255.0).round() as u8
                };

                let output_pixel = Pixel {
                    r: compute(r),
                    g: compute(g),
                    b: compute(b),
                    a: (clamped_a * 255.0).round() as u8,
                };

                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }

        if let Some((dx, dy)) = self.kernel_unit_length.get() {
            // Scale the output surface back.
            let paffine = ctx.paffine();
            let ox = paffine.xx * dx + paffine.xy * dy;
            let oy = paffine.yx * dx + paffine.yy * dy;

            bounds = original_bounds;

            let surface = ImageSurface::create(
                cairo::Format::ARgb32,
                ctx.source_graphic().width(),
                ctx.source_graphic().height(),
            ).map_err(FilterError::OutputSurfaceCreation)?;

            {
                let cr = cairo::Context::new(&surface);
                cr.rectangle(
                    bounds.x0 as f64,
                    bounds.y0 as f64,
                    (bounds.x1 - bounds.x0) as f64,
                    (bounds.y1 - bounds.y0) as f64,
                );
                cr.clip();

                cr.scale(ox, oy);
                cr.set_source_surface(&output_surface, 0.0, 0.0);
                cr.paint();
            }

            output_surface = surface;
        }

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface).unwrap(),
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters() -> bool {
        true
    }
}

impl EdgeMode {
    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
        match s {
            "duplicate" => Ok(EdgeMode::Duplicate),
            "wrap" => Ok(EdgeMode::Wrap),
            "none" => Ok(EdgeMode::None),
            _ => Err(NodeError::parse_error(
                attr,
                ParseError::new("invalid value"),
            )),
        }
    }
}
<commit_msg>Refactor convolve_matrix to use the scale method<commit_after>use std::cell::{Cell, RefCell};

use cairo::{self, ImageSurface};
use rulinalg::matrix::{BaseMatrix, Matrix};

use attributes::Attribute;
use drawing_ctx::DrawingCtx;
use error::NodeError;
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, RsvgCNodeImpl, RsvgNode};
use parsers::{self, ListLength, NumberListError, ParseError};
use property_bag::PropertyBag;
use surface_utils::{
    iterators::{PixelRectangle, Pixels},
    shared_surface::SharedImageSurface,
    EdgeMode,
    ImageSurfaceDataExt,
    Pixel,
};
use util::clamp;

use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
use super::{make_result, Filter, FilterError, PrimitiveWithInput};

/// The `feConvolveMatrix` filter primitive.
pub struct ConvolveMatrix {
    base: PrimitiveWithInput,
    order: Cell<(u32, u32)>,
    kernel_matrix: RefCell<Option<Matrix<f64>>>,
    divisor: Cell<Option<f64>>,
    bias: Cell<f64>,
    target_x: Cell<Option<u32>>,
    target_y: Cell<Option<u32>>,
    edge_mode: Cell<EdgeMode>,
    kernel_unit_length: Cell<Option<(f64, f64)>>,
    preserve_alpha: Cell<bool>,
}

impl ConvolveMatrix {
    /// Constructs a new `ConvolveMatrix` with empty properties.
    #[inline]
    pub fn new() -> ConvolveMatrix {
        ConvolveMatrix {
            base: PrimitiveWithInput::new::<Self>(),
            order: Cell::new((3, 3)),
            kernel_matrix: RefCell::new(None),
            divisor: Cell::new(None),
            bias: Cell::new(0.0),
            target_x: Cell::new(None),
            target_y: Cell::new(None),
            edge_mode: Cell::new(EdgeMode::Duplicate),
            kernel_unit_length: Cell::new(None),
            preserve_alpha: Cell::new(false),
        }
    }
}

impl NodeTrait for ConvolveMatrix {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)?;

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::Order => self.order.set(
                    parsers::integer_optional_integer(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|(x, y)| {
                            if x > 0 && y > 0 {
                                Ok((x as u32, y as u32))
                            } else {
                                Err(NodeError::value_error(
                                    attr,
                                    "values must be greater than 0",
                                ))
                            }
                        })?,
                ),
                Attribute::Divisor => self.divisor.set(Some(
                    parsers::number(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|x| {
                            if x != 0.0 {
                                Ok(x)
                            } else {
                                Err(NodeError::value_error(attr, "divisor cannot be equal to 0"))
                            }
                        })?,
                )),
                Attribute::Bias => self
                    .bias
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                Attribute::EdgeMode => self.edge_mode.set(EdgeMode::parse(attr, value)?),
                Attribute::KernelUnitLength => self.kernel_unit_length.set(Some(
                    parsers::number_optional_number(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|(x, y)| {
                            if x > 0.0 && y > 0.0 {
                                Ok((x, y))
                            } else {
                                Err(NodeError::value_error(
                                    attr,
                                    "kernelUnitLength can't be less or equal to zero",
                                ))
                            }
                        })?,
                )),
                Attribute::PreserveAlpha => self.preserve_alpha.set(match value {
                    "false" => false,
                    "true" => true,
                    _ => {
                        return Err(NodeError::parse_error(
                            attr,
                            ParseError::new("expected false or true"),
                        ))
                    }
                }),
                _ => (),
            }
        }

        // target_x and target_y depend on order.
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::TargetX => self.target_x.set(Some(
                    parsers::integer(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|x| {
                            if x >= 0 && x < self.order.get().0 as i32 {
                                Ok(x as u32)
                            } else {
                                Err(NodeError::value_error(
                                    attr,
                                    "targetX must be greater or equal to zero and less than orderX",
                                ))
                            }
                        })?,
                )),
                Attribute::TargetY => self.target_y.set(Some(
                    parsers::integer(value)
                        .map_err(|err| NodeError::parse_error(attr, err))
                        .and_then(|x| {
                            if x >= 0 && x < self.order.get().1 as i32 {
                                Ok(x as u32)
                            } else {
                                Err(NodeError::value_error(
                                    attr,
                                    "targetY must be greater or equal to zero and less than orderY",
                                ))
                            }
                        })?,
                )),
                _ => (),
            }
        }

        // Default values for target_x and target_y.
        if self.target_x.get().is_none() {
            self.target_x.set(Some(self.order.get().0 / 2));
        }
        if self.target_y.get().is_none() {
            self.target_y.set(Some(self.order.get().1 / 2));
        }

        // Finally, parse the kernel matrix.
        for (_, attr, value) in pbag
            .iter()
            .filter(|(_, attr, _)| *attr == Attribute::KernelMatrix)
        {
            self.kernel_matrix.replace(Some({
                let number_of_elements = self.order.get().0 as usize * self.order.get().1 as usize;

                Matrix::new(
                    self.order.get().1 as usize,
                    self.order.get().0 as usize,
                    parsers::number_list_from_str(value, ListLength::Exact(number_of_elements))
                        .map_err(|err| {
                            NodeError::parse_error(
                                attr,
                                match err {
                                    NumberListError::IncorrectNumberOfElements => {
                                        ParseError::new(format!(
                                            "incorrect number of elements: expected {}",
                                            number_of_elements
                                        ))
                                    }
                                    NumberListError::Parse(err) => err,
                                },
                            )
                        })?,
                )
            }));
        }

        // Default value for the divisor.
        if self.divisor.get().is_none() {
            self.divisor
                .set(Some(self.kernel_matrix.borrow().as_ref().unwrap().sum()));

            if self.divisor.get().unwrap() == 0.0 {
                self.divisor.set(Some(1.0));
            }
        }

        Ok(())
    }

    #[inline]
    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        self.base.get_c_impl()
    }
}

impl Filter for ConvolveMatrix {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = make_result(self.base.get_input(ctx, draw_ctx))?;
        let mut bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .into_irect(draw_ctx);
        let original_bounds = bounds;

        let mut input_surface = if self.preserve_alpha.get() {
            // preserve_alpha means we need to premultiply and unpremultiply the values.
            //
            // HACK: this is storing unpremultiplied pixels in an ARGB32 image surface (which is
            // supposed to be premultiplied pixels).
            let mut unpremultiplied_surface = ImageSurface::create(
                cairo::Format::ARgb32,
                input.surface().width(),
                input.surface().height(),
            ).map_err(FilterError::OutputSurfaceCreation)?;

            let stride = unpremultiplied_surface.get_stride() as usize;
            {
                let mut data = unpremultiplied_surface.get_data().unwrap();

                for (x, y, pixel) in Pixels::new(input.surface(), bounds) {
                    let new_pixel = if pixel.a == 0 {
                        pixel
                    } else {
                        let alpha = f64::from(pixel.a) / 255.0;
                        let unpremultiply = |x| (f64::from(x) / alpha).round() as u8;

                        Pixel {
                            r: unpremultiply(pixel.r),
                            g: unpremultiply(pixel.g),
                            b: unpremultiply(pixel.b),
                            a: pixel.a,
                        }
                    };

                    data.set_pixel(stride, new_pixel, x, y);
                }
            }

            SharedImageSurface::new(unpremultiplied_surface).unwrap()
        } else {
            input.surface().clone()
        };

        let scale = self.kernel_unit_length.get().map(|(dx, dy)| {
            let paffine = ctx.paffine();
            let ox = paffine.xx * dx + paffine.xy * dy;
            let oy = paffine.yx * dx + paffine.yy * dy;

            (ox, oy)
        });

        if let Some((ox, oy)) = scale {
            // Scale the input surface to match kernel_unit_length.
            let (new_surface, new_bounds) = input_surface
                .scale(bounds, 1.0 / ox, 1.0 / oy)
                .map_err(FilterError::OutputSurfaceCreation)?;

            input_surface = new_surface;
            bounds = new_bounds;
        }

        let matrix = self.kernel_matrix.borrow();
        let matrix = matrix.as_ref().unwrap();

        let mut output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            input_surface.width(),
            input_surface.height(),
        ).map_err(FilterError::OutputSurfaceCreation)?;

        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            for (x, y, pixel) in Pixels::new(&input_surface, bounds) {
                // Compute the convolution rectangle bounds.
                let kernel_bounds = IRect {
                    x0: x as i32 - self.target_x.get().unwrap() as i32,
                    y0: y as i32 - self.target_y.get().unwrap() as i32,
                    x1: x as i32 - self.target_x.get().unwrap() as i32 + self.order.get().0 as i32,
                    y1: y as i32 - self.target_y.get().unwrap() as i32 + self.order.get().1 as i32,
                };

                // Do the convolution.
                let mut r = 0.0;
                let mut g = 0.0;
                let mut b = 0.0;
                let mut a = 0.0;

                for (x, y, pixel) in
                    PixelRectangle::new(&input_surface, bounds, kernel_bounds, self.edge_mode.get())
                {
                    let kernel_x = (kernel_bounds.x1 - x - 1) as usize;
                    let kernel_y = (kernel_bounds.y1 - y - 1) as usize;

                    r += f64::from(pixel.r) / 255.0 * matrix[[kernel_y, kernel_x]];
                    g += f64::from(pixel.g) / 255.0 * matrix[[kernel_y, kernel_x]];
                    b += f64::from(pixel.b) / 255.0 * matrix[[kernel_y, kernel_x]];

                    if !self.preserve_alpha.get() {
                        a += f64::from(pixel.a) / 255.0 * matrix[[kernel_y, kernel_x]];
                    }
                }

                // If preserve_alpha is true, set a to the source alpha value.
                if self.preserve_alpha.get() {
                    a = f64::from(pixel.a) / 255.0;
                } else {
                    a = a / self.divisor.get().unwrap() + self.bias.get();
                }

                let clamped_a = clamp(a, 0.0, 1.0);

                let compute = |x| {
                    let x = x / self.divisor.get().unwrap() + self.bias.get() * a;

                    let x = if self.preserve_alpha.get() {
                        // Premultiply the output value.
                        clamp(x, 0.0, 1.0) * clamped_a
                    } else {
                        clamp(x, 0.0, clamped_a)
                    };

                    (x * 255.0).round() as u8
                };

                let output_pixel = Pixel {
                    r: compute(r),
                    g: compute(g),
                    b: compute(b),
                    a: (clamped_a * 255.0).round() as u8,
                };

                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }

        let mut output_surface =
            SharedImageSurface::new(output_surface).map_err(FilterError::OutputSurfaceCreation)?;

        if let Some((ox, oy)) = scale {
            // Scale the output surface back.
            output_surface = output_surface
                .scale_to(
                    ctx.source_graphic().width(),
                    ctx.source_graphic().height(),
                    original_bounds,
                    ox,
                    oy,
                )
                .map_err(FilterError::OutputSurfaceCreation)?;
        }

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: output_surface,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters() -> bool {
        true
    }
}

impl EdgeMode {
    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
        match s {
            "duplicate" => Ok(EdgeMode::Duplicate),
            "wrap" => Ok(EdgeMode::Wrap),
            "none" => Ok(EdgeMode::None),
            _ => Err(NodeError::parse_error(
                attr,
                ParseError::new("invalid value"),
            )),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use aptos_config::config::{
    EpochSnapshotPrunerConfig, LedgerPrunerConfig, PrunerConfig, StateMerklePrunerConfig,
};
use aptos_push_metrics::MetricsPusher;
use aptos_vm::AptosVM;
use std::path::PathBuf;
use structopt::StructOpt;

#[cfg(unix)]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

#[derive(Debug, StructOpt)]
struct PrunerOpt {
    #[structopt(long)]
    enable_state_pruner: bool,

    #[structopt(long)]
    enable_epoch_snapshot_pruner: bool,

    #[structopt(long)]
    enable_ledger_pruner: bool,

    #[structopt(long, default_value = "100000")]
    state_prune_window: u64,

    #[structopt(long, default_value = "100000")]
    epoch_snapshot_prune_window: u64,

    #[structopt(long, default_value = "100000")]
    ledger_prune_window: u64,

    #[structopt(long, default_value = "500")]
    ledger_pruning_batch_size: usize,

    #[structopt(long, default_value = "500")]
    state_pruning_batch_size: usize,

    #[structopt(long, default_value = "500")]
    epoch_snapshot_pruning_batch_size: usize,
}

impl PrunerOpt {
    fn pruner_config(&self) -> PrunerConfig {
        PrunerConfig {
            state_merkle_pruner_config: StateMerklePrunerConfig {
                enable: self.enable_state_pruner,
                prune_window: self.state_prune_window,
                batch_size: self.state_pruning_batch_size,
            },
            epoch_snapshot_pruner_config: EpochSnapshotPrunerConfig {
                enable: self.enable_epoch_snapshot_pruner,
                prune_window: self.epoch_snapshot_prune_window,
                batch_size: self.epoch_snapshot_pruning_batch_size,
            },
            ledger_pruner_config: LedgerPrunerConfig {
                enable: self.enable_ledger_pruner,
                prune_window: self.ledger_prune_window,
                batch_size: self.ledger_pruning_batch_size,
                user_pruning_window_offset: 0,
            },
        }
    }
}

#[derive(Debug, StructOpt)]
struct Opt {
    #[structopt(long, default_value = "500")]
    block_size: usize,

    #[structopt(long)]
    concurrency_level: Option<usize>,

    #[structopt(flatten)]
    pruner_opt: PrunerOpt,

    #[structopt(subcommand)]
    cmd: Command,

    #[structopt(
        long,
        about = "Verify sequence number of all the accounts after execution finishes"
    )]
    verify_sequence_numbers: bool,
}

impl Opt {
    fn concurrency_level(&self) -> usize {
        match self.concurrency_level {
            None => {
                let level = num_cpus::get();
                println!(
                    "\nVM concurrency level defaults to num of cpus: {}\n",
                    level
                );
                level
            }
            Some(level) => level,
        }
    }
}

#[derive(Debug, StructOpt)]
enum Command {
    CreateDb {
        #[structopt(long, parse(from_os_str))]
        data_dir: PathBuf,

        #[structopt(long, default_value = "1000000")]
        num_accounts: usize,

        #[structopt(long, default_value = "1000000")]
        init_account_balance: u64,
    },
    RunExecutor {
        #[structopt(
            long,
            default_value = "1000",
            about = "number of transfer blocks to run"
        )]
        blocks: usize,

        #[structopt(long, parse(from_os_str))]
        data_dir: PathBuf,

        #[structopt(long, parse(from_os_str))]
        checkpoint_dir: PathBuf,
    },
    AddAccounts {
        #[structopt(long, parse(from_os_str))]
        data_dir: PathBuf,

        #[structopt(long, parse(from_os_str))]
        checkpoint_dir: PathBuf,

        #[structopt(long, default_value = "1000000")]
        num_new_accounts: usize,

        #[structopt(long, default_value = "1000000")]
        init_account_balance: u64,
    },
}

fn main() {
    #[allow(deprecated)]
    let _mp = MetricsPusher::start();
    let opt = Opt::from_args();

    aptos_logger::Logger::new().init();

    rayon::ThreadPoolBuilder::new()
        .thread_name(|index| format!("rayon-global-{}", index))
        .build_global()
        .expect("Failed to build rayon global thread pool.");
    AptosVM::set_concurrency_level_once(opt.concurrency_level());

    match opt.cmd {
        Command::CreateDb {
            data_dir,
            num_accounts,
            init_account_balance,
        } => {
            executor_benchmark::db_generator::run(
                num_accounts,
                init_account_balance,
                opt.block_size,
                data_dir,
                opt.pruner_opt.pruner_config(),
                opt.verify_sequence_numbers,
            );
        }
        Command::RunExecutor {
            blocks,
            data_dir,
            checkpoint_dir,
        } => {
            executor_benchmark::run_benchmark(
                opt.block_size,
                blocks,
                data_dir,
                checkpoint_dir,
                opt.verify_sequence_numbers,
                opt.pruner_opt.pruner_config(),
            );
        }
        Command::AddAccounts {
            data_dir,
            checkpoint_dir,
            num_new_accounts,
            init_account_balance,
        } => {
            executor_benchmark::add_accounts(
                num_new_accounts,
                init_account_balance,
                opt.block_size,
                data_dir,
                checkpoint_dir,
                opt.pruner_opt.pruner_config(),
                opt.verify_sequence_numbers,
            );
        }
    }
}
<commit_msg>[executor-benchmark] more default initial balance<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use aptos_config::config::{
    EpochSnapshotPrunerConfig, LedgerPrunerConfig, PrunerConfig, StateMerklePrunerConfig,
};
use aptos_push_metrics::MetricsPusher;
use aptos_vm::AptosVM;
use std::path::PathBuf;
use structopt::StructOpt;

#[cfg(unix)]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

#[derive(Debug, StructOpt)]
struct PrunerOpt {
    #[structopt(long)]
    enable_state_pruner: bool,

    #[structopt(long)]
    enable_epoch_snapshot_pruner: bool,

    #[structopt(long)]
    enable_ledger_pruner: bool,

    #[structopt(long, default_value = "100000")]
    state_prune_window: u64,

    #[structopt(long, default_value = "100000")]
    epoch_snapshot_prune_window: u64,

    #[structopt(long, default_value = "100000")]
    ledger_prune_window: u64,

    #[structopt(long, default_value = "500")]
    ledger_pruning_batch_size: usize,

    #[structopt(long, default_value = "500")]
    state_pruning_batch_size: usize,

    #[structopt(long, default_value = "500")]
    epoch_snapshot_pruning_batch_size: usize,
}

impl PrunerOpt {
    fn pruner_config(&self) -> PrunerConfig {
        PrunerConfig {
            state_merkle_pruner_config: StateMerklePrunerConfig {
                enable: self.enable_state_pruner,
                prune_window: self.state_prune_window,
                batch_size: self.state_pruning_batch_size,
            },
            epoch_snapshot_pruner_config: EpochSnapshotPrunerConfig {
                enable: self.enable_epoch_snapshot_pruner,
                prune_window: self.epoch_snapshot_prune_window,
                batch_size: self.epoch_snapshot_pruning_batch_size,
            },
            ledger_pruner_config: LedgerPrunerConfig {
                enable: self.enable_ledger_pruner,
                prune_window: self.ledger_prune_window,
                batch_size: self.ledger_pruning_batch_size,
                user_pruning_window_offset: 0,
            },
        }
    }
}

#[derive(Debug, StructOpt)]
struct Opt {
    #[structopt(long, default_value = "500")]
    block_size: usize,

    #[structopt(long)]
    concurrency_level: Option<usize>,

    #[structopt(flatten)]
    pruner_opt: PrunerOpt,

    #[structopt(subcommand)]
    cmd: Command,

    #[structopt(
        long,
        about = "Verify sequence number of all the accounts after execution finishes"
    )]
    verify_sequence_numbers: bool,
}

impl Opt {
    fn concurrency_level(&self) -> usize {
        match self.concurrency_level {
            None => {
                let level = num_cpus::get();
                println!(
                    "\nVM concurrency level defaults to num of cpus: {}\n",
                    level
                );
                level
            }
            Some(level) => level,
        }
    }
}

#[derive(Debug, StructOpt)]
enum Command {
    CreateDb {
        #[structopt(long, parse(from_os_str))]
        data_dir: PathBuf,

        #[structopt(long, default_value = "1000000")]
        num_accounts: usize,

        #[structopt(long, default_value = "10000000000")]
        init_account_balance: u64,
    },
    RunExecutor {
        #[structopt(
            long,
            default_value = "1000",
            about = "number of transfer blocks to run"
        )]
        blocks: usize,

        #[structopt(long, parse(from_os_str))]
        data_dir: PathBuf,

        #[structopt(long, parse(from_os_str))]
        checkpoint_dir: PathBuf,
    },
    AddAccounts {
        #[structopt(long, parse(from_os_str))]
        data_dir: PathBuf,

        #[structopt(long, parse(from_os_str))]
        checkpoint_dir: PathBuf,

        #[structopt(long, default_value = "1000000")]
        num_new_accounts: usize,

        #[structopt(long, default_value = "1000000")]
        init_account_balance: u64,
    },
}

fn main() {
    #[allow(deprecated)]
    let _mp = MetricsPusher::start();
    let opt = Opt::from_args();

    aptos_logger::Logger::new().init();

    rayon::ThreadPoolBuilder::new()
        .thread_name(|index| format!("rayon-global-{}", index))
        .build_global()
        .expect("Failed to build rayon global thread pool.");
    AptosVM::set_concurrency_level_once(opt.concurrency_level());

    match opt.cmd {
        Command::CreateDb {
            data_dir,
            num_accounts,
            init_account_balance,
        } => {
            executor_benchmark::db_generator::run(
                num_accounts,
                init_account_balance,
                opt.block_size,
                data_dir,
                opt.pruner_opt.pruner_config(),
                opt.verify_sequence_numbers,
            );
        }
        Command::RunExecutor {
            blocks,
            data_dir,
            checkpoint_dir,
        } => {
            executor_benchmark::run_benchmark(
                opt.block_size,
                blocks,
                data_dir,
                checkpoint_dir,
                opt.verify_sequence_numbers,
                opt.pruner_opt.pruner_config(),
            );
        }
        Command::AddAccounts {
            data_dir,
            checkpoint_dir,
            num_new_accounts,
            init_account_balance,
        } => {
            executor_benchmark::add_accounts(
                num_new_accounts,
                init_account_balance,
                opt.block_size,
                data_dir,
                checkpoint_dir,
                opt.pruner_opt.pruner_config(),
                opt.verify_sequence_numbers,
            );
        }
    }
}
<|endoftext|>


<commit_before>#![feature(slice_concat_ext)]

extern crate compiletest_rs as compiletest;

use std::slice::SliceConcatExt;
use std::path::{PathBuf, Path};
use std::io::Write;

macro_rules! eprintln {
    ($($arg:tt)*) => {
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), $($arg)*).unwrap();
    }
}

fn miri_path() -> PathBuf {
    if rustc_test_suite().is_some() {
        PathBuf::from(option_env!("MIRI_PATH").unwrap())
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/miri"))
    }
}

fn rustc_test_suite() -> Option<PathBuf> {
    option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}

fn rustc_lib_path() -> PathBuf {
    option_env!("RUSTC_LIB_PATH").unwrap().into()
}

fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, fullmir: bool) {
    eprintln!(
        "## Running compile-fail tests in {} against miri for target {}",
        path,
        target
    );
    let mut config = compiletest::default_config();
    config.mode = "compile-fail".parse().expect("Invalid mode");
    config.rustc_path = miri_path();
    let mut flags = Vec::new();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    // if we are building as part of the rustc test suite, we already have fullmir for everything
    if fullmir && rustc_test_suite().is_none() {
        if host != target {
            // skip fullmir on nonhost
            return;
        }
        let sysroot = Path::new(&std::env::var("HOME").unwrap())
            .join(".xargo")
            .join("HOST");
        config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap()));
        config.src_base = PathBuf::from(path.to_string());
    } else {
        config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap()));
        config.src_base = PathBuf::from(path.to_string());
    }
    flags.push("-Zmir-emit-validate=1".to_owned());
    config.target_rustcflags = Some(flags.join(" "));
    config.target = target.to_owned();
    compiletest::run_tests(&config);
}

fn run_pass(path: &str) {
    eprintln!("## Running run-pass tests in {} against rustc", path);
    let mut config = compiletest::default_config();
    config.mode = "run-pass".parse().expect("Invalid mode");
    config.src_base = PathBuf::from(path);
    if let Some(rustc_path) = rustc_test_suite() {
        config.rustc_path = rustc_path;
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
        config.target_rustcflags = Some(format!("-Dwarnings --sysroot {}", get_sysroot().display()));
    } else {
        config.target_rustcflags = Some("-Dwarnings".to_owned());
    }
    config.host_rustcflags = Some("-Dwarnings".to_string());
    compiletest::run_tests(&config);
}

fn miri_pass(path: &str, target: &str, host: &str, fullmir: bool, opt: bool) {
    let opt_str = if opt { " with optimizations" } else { "" };
    eprintln!(
        "## Running run-pass tests in {} against miri for target {}{}",
        path,
        target,
        opt_str
    );
    let mut config = compiletest::default_config();
    config.mode = "mir-opt".parse().expect("Invalid mode");
    config.src_base = PathBuf::from(path);
    config.target = target.to_owned();
    config.host = host.to_owned();
    config.rustc_path = miri_path();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    let mut flags = Vec::new();
    // if we are building as part of the rustc test suite, we already have fullmir for everything
    if fullmir && rustc_test_suite().is_none() {
        if host != target {
            // skip fullmir on nonhost
            return;
        }
        let sysroot = Path::new(&std::env::var("HOME").unwrap())
            .join(".xargo")
            .join("HOST");
        flags.push(format!("--sysroot {}", sysroot.to_str().unwrap()));
    }
    if opt {
        flags.push("-Zmir-opt-level=3".to_owned());
    } else {
        flags.push("-Zmir-opt-level=0".to_owned());
        // For now, only validate without optimizations.  Inlining breaks validation.
        flags.push("-Zmir-emit-validate=1".to_owned());
    }
    config.target_rustcflags = Some(flags.join(" "));
    // don't actually execute the final binary, it might be for other targets and we only care
    // about running miri, not the binary.
    config.runtool = Some("echo \"\" || ".to_owned());
    if target == host {
        std::env::set_var("MIRI_HOST_TARGET", "yes");
    }
    compiletest::run_tests(&config);
    std::env::set_var("MIRI_HOST_TARGET", "");
}

fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
    let mut path = path.into();
    path.push("lib");
    path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

fn for_all_targets<F: FnMut(String)>(sysroot: &Path, mut f: F) {
    let target_dir = sysroot.join("lib").join("rustlib");
    for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") {
        let entry = entry.unwrap();
        if !is_target_dir(entry.path()) {
            continue;
        }
        let target = entry.file_name().into_string().unwrap();
        f(target);
    }
}

fn get_sysroot() -> PathBuf {
    let sysroot = std::env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
        let sysroot = std::process::Command::new("rustc")
            .arg("--print")
            .arg("sysroot")
            .output()
            .expect("rustc not found")
            .stdout;
        String::from_utf8(sysroot).expect("sysroot is not utf8")
    });
    PathBuf::from(sysroot.trim())
}

fn get_host() -> String {
    let host = std::process::Command::new("rustc")
        .arg("-vV")
        .output()
        .expect("rustc not found for -vV")
        .stdout;
    let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
    let host = host.split("\nhost: ").nth(1).expect(
        "no host: part in rustc -vV",
    );
    let host = host.split('\n').next().expect("no \n after host");
    String::from(host)
}

#[test]
fn run_pass_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    for &opt in [false, true].iter() {
        for_all_targets(&sysroot, |target| {
            miri_pass("tests/run-pass", &target, &host, false, opt);
        });
        miri_pass("tests/run-pass-fullmir", &host, &host, true, opt);
    }
}

#[test]
fn run_pass_rustc() {
    run_pass("tests/run-pass");
    run_pass("tests/run-pass-fullmir");
}

#[test]
fn compile_fail_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    for_all_targets(&sysroot, |target| {
        compile_fail(&sysroot, "tests/compile-fail", &target, &host, false);
    });
    compile_fail(&sysroot, "tests/compile-fail-fullmir", &host, &host, true);
}
<commit_msg>move tests with MIR-opt to their own function we we can run them separately<commit_after>#![feature(slice_concat_ext)]

extern crate compiletest_rs as compiletest;

use std::slice::SliceConcatExt;
use std::path::{PathBuf, Path};
use std::io::Write;

macro_rules! eprintln {
    ($($arg:tt)*) => {
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), $($arg)*).unwrap();
    }
}

fn miri_path() -> PathBuf {
    if rustc_test_suite().is_some() {
        PathBuf::from(option_env!("MIRI_PATH").unwrap())
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/miri"))
    }
}

fn rustc_test_suite() -> Option<PathBuf> {
    option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}

fn rustc_lib_path() -> PathBuf {
    option_env!("RUSTC_LIB_PATH").unwrap().into()
}

fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, fullmir: bool) {
    eprintln!(
        "## Running compile-fail tests in {} against miri for target {}",
        path,
        target
    );
    let mut config = compiletest::default_config();
    config.mode = "compile-fail".parse().expect("Invalid mode");
    config.rustc_path = miri_path();
    let mut flags = Vec::new();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    // if we are building as part of the rustc test suite, we already have fullmir for everything
    if fullmir && rustc_test_suite().is_none() {
        if host != target {
            // skip fullmir on nonhost
            return;
        }
        let sysroot = Path::new(&std::env::var("HOME").unwrap())
            .join(".xargo")
            .join("HOST");
        config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap()));
        config.src_base = PathBuf::from(path.to_string());
    } else {
        config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap()));
        config.src_base = PathBuf::from(path.to_string());
    }
    flags.push("-Zmir-emit-validate=1".to_owned());
    config.target_rustcflags = Some(flags.join(" "));
    config.target = target.to_owned();
    compiletest::run_tests(&config);
}

fn run_pass(path: &str) {
    eprintln!("## Running run-pass tests in {} against rustc", path);
    let mut config = compiletest::default_config();
    config.mode = "run-pass".parse().expect("Invalid mode");
    config.src_base = PathBuf::from(path);
    if let Some(rustc_path) = rustc_test_suite() {
        config.rustc_path = rustc_path;
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
        config.target_rustcflags = Some(format!("-Dwarnings --sysroot {}", get_sysroot().display()));
    } else {
        config.target_rustcflags = Some("-Dwarnings".to_owned());
    }
    config.host_rustcflags = Some("-Dwarnings".to_string());
    compiletest::run_tests(&config);
}

fn miri_pass(path: &str, target: &str, host: &str, fullmir: bool, opt: bool) {
    let opt_str = if opt { " with optimizations" } else { "" };
    eprintln!(
        "## Running run-pass tests in {} against miri for target {}{}",
        path,
        target,
        opt_str
    );
    let mut config = compiletest::default_config();
    config.mode = "mir-opt".parse().expect("Invalid mode");
    config.src_base = PathBuf::from(path);
    config.target = target.to_owned();
    config.host = host.to_owned();
    config.rustc_path = miri_path();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    let mut flags = Vec::new();
    // if we are building as part of the rustc test suite, we already have fullmir for everything
    if fullmir && rustc_test_suite().is_none() {
        if host != target {
            // skip fullmir on nonhost
            return;
        }
        let sysroot = Path::new(&std::env::var("HOME").unwrap())
            .join(".xargo")
            .join("HOST");
        flags.push(format!("--sysroot {}", sysroot.to_str().unwrap()));
    }
    if opt {
        flags.push("-Zmir-opt-level=3".to_owned());
    } else {
        flags.push("-Zmir-opt-level=0".to_owned());
        // For now, only validate without optimizations.  Inlining breaks validation.
        flags.push("-Zmir-emit-validate=1".to_owned());
    }
    config.target_rustcflags = Some(flags.join(" "));
    // don't actually execute the final binary, it might be for other targets and we only care
    // about running miri, not the binary.
    config.runtool = Some("echo \"\" || ".to_owned());
    if target == host {
        std::env::set_var("MIRI_HOST_TARGET", "yes");
    }
    compiletest::run_tests(&config);
    std::env::set_var("MIRI_HOST_TARGET", "");
}

fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
    let mut path = path.into();
    path.push("lib");
    path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

fn for_all_targets<F: FnMut(String)>(sysroot: &Path, mut f: F) {
    let target_dir = sysroot.join("lib").join("rustlib");
    for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") {
        let entry = entry.unwrap();
        if !is_target_dir(entry.path()) {
            continue;
        }
        let target = entry.file_name().into_string().unwrap();
        f(target);
    }
}

fn get_sysroot() -> PathBuf {
    let sysroot = std::env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
        let sysroot = std::process::Command::new("rustc")
            .arg("--print")
            .arg("sysroot")
            .output()
            .expect("rustc not found")
            .stdout;
        String::from_utf8(sysroot).expect("sysroot is not utf8")
    });
    PathBuf::from(sysroot.trim())
}

fn get_host() -> String {
    let host = std::process::Command::new("rustc")
        .arg("-vV")
        .output()
        .expect("rustc not found for -vV")
        .stdout;
    let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
    let host = host.split("\nhost: ").nth(1).expect(
        "no host: part in rustc -vV",
    );
    let host = host.split('\n').next().expect("no \n after host");
    String::from(host)
}

fn run_pass_miri(opt: bool) {
    let sysroot = get_sysroot();
    let host = get_host();

    for_all_targets(&sysroot, |target| {
        miri_pass("tests/run-pass", &target, &host, false, opt);
    });
    miri_pass("tests/run-pass-fullmir", &host, &host, true, opt);
}

#[test]
fn run_pass_miri_noopt() {
    run_pass_miri(false);
}

#[test]
fn run_pass_miri_opt() {
    run_pass_miri(true);
}

#[test]
fn run_pass_rustc() {
    run_pass("tests/run-pass");
    run_pass("tests/run-pass-fullmir");
}

#[test]
fn compile_fail_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    for_all_targets(&sysroot, |target| {
        compile_fail(&sysroot, "tests/compile-fail", &target, &host, false);
    });
    compile_fail(&sysroot, "tests/compile-fail-fullmir", &host, &host, true);
}
<|endoftext|>


<commit_before>extern crate libc;

use std::sync::mpsc::{Receiver, Sender, SyncSender, channel};
use std::error::Error;
use item::Item;
use std::sync::{Arc, RwLock};
use std::process::{Command, Stdio, Child};
use std::io::{stdin, BufRead, BufReader};
use event::{Event, EventArg};
use std::thread::JoinHandle;
use std::thread;
use std::time::Duration;
use std::collections::HashMap;
use std::mem;

use getopts;
use regex::Regex;
use sender::CachedSender;

macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

struct ReaderOption {
    pub use_ansi_color: bool,
    pub default_arg: String,
    pub transform_fields: Vec<FieldRange>,
    pub matching_fields: Vec<FieldRange>,
    pub delimiter: Regex,
    pub replace_str: String,
}

impl ReaderOption {
    pub fn new() -> Self {
        ReaderOption {
            use_ansi_color: false,
            default_arg: String::new(),
            transform_fields: Vec::new(),
            matching_fields: Vec::new(),
            delimiter: Regex::new(r".*?\t").unwrap(),
            replace_str: "{}".to_string(),
        }
    }

    pub fn parse_options(&mut self, options: &getopts::Matches) {
        if options.opt_present("ansi") {
            self.use_ansi_color = true;
        }

        if let Some(delimiter) = options.opt_str("d") {
            self.delimiter = Regex::new(&(".*?".to_string() + &delimiter))
                .unwrap_or(Regex::new(r".*?\t").unwrap());
        }

        if let Some(transform_fields) = options.opt_str("with-nth") {
            self.transform_fields = transform_fields.split(',')
                .filter_map(|string| {
                    parse_range(string)
                })
                .collect();
        }

        if let Some(matching_fields) = options.opt_str("nth") {
            self.matching_fields = matching_fields.split(',')
                .filter_map(|string| {
                    parse_range(string)
                })
                .collect();
        }
    }
}

pub struct Reader {
    rx_cmd: Receiver<(Event, EventArg)>,
    tx_item: SyncSender<(Event, EventArg)>,
    option: Arc<RwLock<ReaderOption>>,
}

impl Reader {
    pub fn new(rx_cmd: Receiver<(Event, EventArg)>, tx_item: SyncSender<(Event, EventArg)>) -> Self {
        Reader {
            rx_cmd: rx_cmd,
            tx_item: tx_item,
            option: Arc::new(RwLock::new(ReaderOption::new()))
        }
    }

    pub fn parse_options(&mut self, options: &getopts::Matches) {
        let mut option = self.option.write().unwrap();
        option.parse_options(options);
    }

    pub fn run(&mut self) {
        // event loop
        let mut thread_reader: Option<JoinHandle<()>> = None;
        let mut tx_reader: Option<Sender<bool>> = None;

        let mut last_command = "".to_string();
        let mut last_query = "".to_string();

        // start sender
        let (tx_sender, rx_sender) = channel();
        let tx_item = self.tx_item.clone();
        let mut sender = CachedSender::new(rx_sender, tx_item);
        thread::spawn(move || {
            sender.run();
        });

        while let Ok((ev, arg)) = self.rx_cmd.recv() {
            match ev {
                Event::EvReaderRestart => {
                    // close existing command or file if exists
                    let (cmd, query, force_update) = *arg.downcast::<(String, String, bool)>().unwrap();
                    if !force_update && cmd == last_command && query == last_query { continue; }

                    // restart command with new `command`
                    if cmd != last_command {
                        // stop existing command
                        tx_reader.take().map(|tx| {tx.send(true)});
                        thread_reader.take().map(|thrd| {thrd.join()});

                        // create needed data for thread
                        let (tx, rx_reader) = channel();
                        tx_reader = Some(tx);
                        let cmd_clone = cmd.clone();
                        let option_clone = self.option.clone();
                        let tx_sender_clone = tx_sender.clone();
                        let query_clone = query.clone();

                        // start the new command
                        thread_reader = Some(thread::spawn(move || {
                            let _ = tx_sender_clone.send((Event::EvReaderStarted, Box::new(true)));
                            let _ = tx_sender_clone.send((Event::EvSenderRestart, Box::new(query_clone)));

                            reader(&cmd_clone, rx_reader, &tx_sender_clone, option_clone);

                            let _ = tx_sender_clone.send((Event::EvReaderStopped, Box::new(true)));
                        }));
                    } else {
                        // tell sender to restart
                        let _ = tx_sender.send((Event::EvSenderRestart, Box::new(query.clone())));
                    }

                    last_command = cmd;
                    last_query = query;
                }

                Event::EvActAccept => {
                    // stop existing command
                    tx_reader.take().map(|tx| {tx.send(true)});
                    thread_reader.take().map(|thrd| {thrd.join()});
                    let tx_ack: Sender<usize> = *arg.downcast().unwrap();
                    let _ = tx_ack.send(0);
                }

                _ => {
                    // do nothing
                }
            }
        }
    }
}

fn get_command_output(cmd: &str) -> Result<(Option<Child>, Box<BufRead>), Box<Error>> {
    let mut command = try!(Command::new("sh")
                       .arg("-c")
                       .arg(cmd)
                       .stdout(Stdio::piped())
                       .stderr(Stdio::null())
                       .spawn());
    let stdout = try!(command.stdout.take().ok_or("command output: unwrap failed".to_owned()));
    Ok((Some(command), Box::new(BufReader::new(stdout))))
}

// Consider that you invoke a command with different arguments several times
// If you select some items each time, how will skim remeber it?
// => Well, we'll give each invokation a number, i.e. RUN_NUM
// What if you invoke the same command and same arguments twice?
// => We use NUM_MAP to specify the same run number.
lazy_static! {
    static ref RUN_NUM: RwLock<usize> = RwLock::new(0);
    static ref NUM_MAP: RwLock<HashMap<String, usize>> = RwLock::new(HashMap::new());
}

fn reader(cmd: &str,
          rx_cmd: Receiver<bool>,
          tx_sender: &Sender<(Event, EventArg)>,
          option: Arc<RwLock<ReaderOption>>) {
    let istty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;

    let (command, mut source): (Option<Child>, Box<BufRead>) = if istty {
        get_command_output(cmd).expect("command not found")
    } else {
        (None, Box::new(BufReader::new(stdin())))
    };

    let (tx_control, rx_control) = channel();

    thread::spawn(move || {
        // listen to `rx` for command to quit reader
        // kill command if it is got
        loop {
            if rx_cmd.try_recv().is_ok() {
                // clean up resources
                command.map(|mut x| {
                    let _ = x.kill();
                    let _ = x.wait();
                });
                break;
            }

            if rx_control.recv_timeout(Duration::from_millis(10)).is_ok() {
                command.map(|mut x| {
                    let _ = x.kill();
                    let _ = x.wait();
                });
                break;
            }
        }
    });

    let opt = option.read().unwrap();

    // set the proper run number
    let run_num = {*RUN_NUM.read().unwrap()};
    let run_num = *NUM_MAP.write()
            .unwrap()
            .entry(cmd.to_string())
            .or_insert_with(|| {
                *(RUN_NUM.write().unwrap()) = run_num + 1;
                run_num + 1
            });

    let mut index = 0;
    let mut item_group = Vec::new();
    loop {
        // start reading
        let mut input = String::new();
        match source.read_line(&mut input) {
            Ok(n) => {
                if n == 0 { break; }

                if input.ends_with('\n') {
                    input.pop();
                    if input.ends_with('\r') {
                        input.pop();
                    }
                }
                let item = Item::new(input,
                                     opt.use_ansi_color,
                                     &opt.transform_fields,
                                     &opt.matching_fields,
                                     &opt.delimiter,
                                     (run_num, index));
                item_group.push(Arc::new(item));
                index += 1;

                if index & 0xFFF == 0 {
                    let _ = tx_sender.send((Event::EvReaderNewItem, Box::new(mem::replace(&mut item_group, Vec::new()))));
                }
            }
            Err(_err) => {} // String not UTF8 or other error, skip.
        }
    }

    if !item_group.is_empty() {
        let _ = tx_sender.send((Event::EvReaderNewItem, Box::new(mem::replace(&mut item_group, Vec::new()))));
    }

    let _ = tx_control.send(true);
}

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum FieldRange {
    Single(i64),
    LeftInf(i64),
    RightInf(i64),
    Both(i64, i64),
}

// range: "start..end", end is excluded.
// "0", "0..", "..10", "1..10", etc.
fn parse_range(range: &str) -> Option<FieldRange> {
    use self::FieldRange::*;

    if range == ".." {
        return Some(RightInf(0));
    }

    let range_string: Vec<&str> = range.split("..").collect();
    if range_string.is_empty() || range_string.len() > 2 {
        return None;
    }

    let start = range_string.get(0).and_then(|x| x.parse::<i64>().ok());
    let end = range_string.get(1).and_then(|x| x.parse::<i64>().ok());

    if range_string.len() == 1 {
        return if start.is_none() {None} else {Some(Single(start.unwrap()))};
    }

    if start.is_none() && end.is_none() {
        None
    } else if end.is_none() {
        // 1..
        Some(RightInf(start.unwrap()))
    } else if start.is_none() {
        // ..1
        Some(LeftInf(end.unwrap()))
    } else {
        Some(Both(start.unwrap(), end.unwrap()))
    }
}

#[cfg(test)]
mod test {
    use super::FieldRange::*;
    #[test]
    fn test_parse_range() {
        assert_eq!(super::parse_range("1"), Some(Single(1)));
        assert_eq!(super::parse_range("-1"), Some(Single(-1)));

        assert_eq!(super::parse_range("1.."), Some(RightInf(1)));
        assert_eq!(super::parse_range("-1.."), Some(RightInf(-1)));

        assert_eq!(super::parse_range("..1"), Some(LeftInf(1)));
        assert_eq!(super::parse_range("..-1"), Some(LeftInf(-1)));

        assert_eq!(super::parse_range("1..3"), Some(Both(1, 3)));
        assert_eq!(super::parse_range("-1..-3"), Some(Both(-1, -3)));

        assert_eq!(super::parse_range(".."), Some(RightInf(0)));
        assert_eq!(super::parse_range("a.."), None);
        assert_eq!(super::parse_range("..b"), None);
        assert_eq!(super::parse_range("a..b"), None);
    }
}
<commit_msg>dup and bind stdin to /dev/tty if not already is<commit_after>extern crate libc;

use std::sync::mpsc::{Receiver, Sender, SyncSender, channel};
use std::error::Error;
use item::Item;
use std::sync::{Arc, RwLock};
use std::process::{Command, Stdio, Child};
use std::io::{stdin, BufRead, BufReader};
use event::{Event, EventArg};
use std::thread::JoinHandle;
use std::thread;
use std::time::Duration;
use std::collections::HashMap;
use std::mem;
use std::fs::File;

use std::os::unix::io::{FromRawFd, IntoRawFd};

use getopts;
use regex::Regex;
use sender::CachedSender;

macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

struct ReaderOption {
    pub use_ansi_color: bool,
    pub default_arg: String,
    pub transform_fields: Vec<FieldRange>,
    pub matching_fields: Vec<FieldRange>,
    pub delimiter: Regex,
    pub replace_str: String,
}

impl ReaderOption {
    pub fn new() -> Self {
        ReaderOption {
            use_ansi_color: false,
            default_arg: String::new(),
            transform_fields: Vec::new(),
            matching_fields: Vec::new(),
            delimiter: Regex::new(r".*?\t").unwrap(),
            replace_str: "{}".to_string(),
        }
    }

    pub fn parse_options(&mut self, options: &getopts::Matches) {
        if options.opt_present("ansi") {
            self.use_ansi_color = true;
        }

        if let Some(delimiter) = options.opt_str("d") {
            self.delimiter = Regex::new(&(".*?".to_string() + &delimiter))
                .unwrap_or(Regex::new(r".*?\t").unwrap());
        }

        if let Some(transform_fields) = options.opt_str("with-nth") {
            self.transform_fields = transform_fields.split(',')
                .filter_map(|string| {
                    parse_range(string)
                })
                .collect();
        }

        if let Some(matching_fields) = options.opt_str("nth") {
            self.matching_fields = matching_fields.split(',')
                .filter_map(|string| {
                    parse_range(string)
                })
                .collect();
        }
    }
}

pub struct Reader {
    rx_cmd: Receiver<(Event, EventArg)>,
    tx_item: SyncSender<(Event, EventArg)>,
    option: Arc<RwLock<ReaderOption>>,
}

impl Reader {
    pub fn new(rx_cmd: Receiver<(Event, EventArg)>, tx_item: SyncSender<(Event, EventArg)>) -> Self {
        Reader {
            rx_cmd: rx_cmd,
            tx_item: tx_item,
            option: Arc::new(RwLock::new(ReaderOption::new()))
        }
    }

    pub fn parse_options(&mut self, options: &getopts::Matches) {
        let mut option = self.option.write().unwrap();
        option.parse_options(options);
    }

    pub fn run(&mut self) {
        // event loop
        let mut thread_reader: Option<JoinHandle<()>> = None;
        let mut tx_reader: Option<Sender<bool>> = None;

        let mut last_command = "".to_string();
        let mut last_query = "".to_string();

        // start sender
        let (tx_sender, rx_sender) = channel();
        let tx_item = self.tx_item.clone();
        let mut sender = CachedSender::new(rx_sender, tx_item);
        thread::spawn(move || {
            sender.run();
        });

        while let Ok((ev, arg)) = self.rx_cmd.recv() {
            match ev {
                Event::EvReaderRestart => {
                    // close existing command or file if exists
                    let (cmd, query, force_update) = *arg.downcast::<(String, String, bool)>().unwrap();
                    if !force_update && cmd == last_command && query == last_query { continue; }

                    // restart command with new `command`
                    if cmd != last_command {
                        // stop existing command
                        tx_reader.take().map(|tx| {tx.send(true)});
                        thread_reader.take().map(|thrd| {thrd.join()});

                        // create needed data for thread
                        let (tx, rx_reader) = channel();
                        tx_reader = Some(tx);
                        let cmd_clone = cmd.clone();
                        let option_clone = self.option.clone();
                        let tx_sender_clone = tx_sender.clone();
                        let query_clone = query.clone();

                        // start the new command
                        thread_reader = Some(thread::spawn(move || {
                            let _ = tx_sender_clone.send((Event::EvReaderStarted, Box::new(true)));
                            let _ = tx_sender_clone.send((Event::EvSenderRestart, Box::new(query_clone)));

                            reader(&cmd_clone, rx_reader, &tx_sender_clone, option_clone);

                            let _ = tx_sender_clone.send((Event::EvReaderStopped, Box::new(true)));
                        }));
                    } else {
                        // tell sender to restart
                        let _ = tx_sender.send((Event::EvSenderRestart, Box::new(query.clone())));
                    }

                    last_command = cmd;
                    last_query = query;
                }

                Event::EvActAccept => {
                    // stop existing command
                    tx_reader.take().map(|tx| {tx.send(true)});
                    thread_reader.take().map(|thrd| {thrd.join()});
                    let tx_ack: Sender<usize> = *arg.downcast().unwrap();
                    let _ = tx_ack.send(0);
                }

                _ => {
                    // do nothing
                }
            }
        }
    }
}

fn get_command_output(cmd: &str) -> Result<(Option<Child>, Box<BufRead>), Box<Error>> {
    let mut command = try!(Command::new("sh")
                       .arg("-c")
                       .arg(cmd)
                       .stdout(Stdio::piped())
                       .stderr(Stdio::null())
                       .spawn());
    let stdout = try!(command.stdout.take().ok_or("command output: unwrap failed".to_owned()));
    Ok((Some(command), Box::new(BufReader::new(stdout))))
}

// Consider that you invoke a command with different arguments several times
// If you select some items each time, how will skim remeber it?
// => Well, we'll give each invokation a number, i.e. RUN_NUM
// What if you invoke the same command and same arguments twice?
// => We use NUM_MAP to specify the same run number.
lazy_static! {
    static ref RUN_NUM: RwLock<usize> = RwLock::new(0);
    static ref NUM_MAP: RwLock<HashMap<String, usize>> = RwLock::new(HashMap::new());
}

fn reader(cmd: &str,
          rx_cmd: Receiver<bool>,
          tx_sender: &Sender<(Event, EventArg)>,
          option: Arc<RwLock<ReaderOption>>) {
    let istty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;

    let (command, mut source): (Option<Child>, Box<BufRead>) = if istty {
        get_command_output(cmd).expect("command not found")
    } else {
        // termion required the stdin to be type tty, so we use dup to achieve that
        unsafe {
            let stdin = File::from_raw_fd(libc::dup(libc::STDIN_FILENO));
            let tty = File::open("/dev/tty").unwrap();
            libc::dup2(tty.into_raw_fd(), libc::STDIN_FILENO);
            (None, Box::new(BufReader::new(stdin)))
        }
    };

    let (tx_control, rx_control) = channel();

    thread::spawn(move || {
        // listen to `rx` for command to quit reader
        // kill command if it is got
        loop {
            if rx_cmd.try_recv().is_ok() {
                // clean up resources
                command.map(|mut x| {
                    let _ = x.kill();
                    let _ = x.wait();
                });
                break;
            }

            if rx_control.recv_timeout(Duration::from_millis(10)).is_ok() {
                command.map(|mut x| {
                    let _ = x.kill();
                    let _ = x.wait();
                });
                break;
            }
        }
    });

    let opt = option.read().unwrap();

    // set the proper run number
    let run_num = {*RUN_NUM.read().unwrap()};
    let run_num = *NUM_MAP.write()
            .unwrap()
            .entry(cmd.to_string())
            .or_insert_with(|| {
                *(RUN_NUM.write().unwrap()) = run_num + 1;
                run_num + 1
            });

    let mut index = 0;
    let mut item_group = Vec::new();
    loop {
        // start reading
        let mut input = String::new();
        match source.read_line(&mut input) {
            Ok(n) => {
                if n == 0 { break; }

                if input.ends_with('\n') {
                    input.pop();
                    if input.ends_with('\r') {
                        input.pop();
                    }
                }
                let item = Item::new(input,
                                     opt.use_ansi_color,
                                     &opt.transform_fields,
                                     &opt.matching_fields,
                                     &opt.delimiter,
                                     (run_num, index));
                item_group.push(Arc::new(item));
                index += 1;

                if index & 0xFFF == 0 {
                    let _ = tx_sender.send((Event::EvReaderNewItem, Box::new(mem::replace(&mut item_group, Vec::new()))));
                }
            }
            Err(_err) => {} // String not UTF8 or other error, skip.
        }
    }

    if !item_group.is_empty() {
        let _ = tx_sender.send((Event::EvReaderNewItem, Box::new(mem::replace(&mut item_group, Vec::new()))));
    }

    let _ = tx_control.send(true);
}

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum FieldRange {
    Single(i64),
    LeftInf(i64),
    RightInf(i64),
    Both(i64, i64),
}

// range: "start..end", end is excluded.
// "0", "0..", "..10", "1..10", etc.
fn parse_range(range: &str) -> Option<FieldRange> {
    use self::FieldRange::*;

    if range == ".." {
        return Some(RightInf(0));
    }

    let range_string: Vec<&str> = range.split("..").collect();
    if range_string.is_empty() || range_string.len() > 2 {
        return None;
    }

    let start = range_string.get(0).and_then(|x| x.parse::<i64>().ok());
    let end = range_string.get(1).and_then(|x| x.parse::<i64>().ok());

    if range_string.len() == 1 {
        return if start.is_none() {None} else {Some(Single(start.unwrap()))};
    }

    if start.is_none() && end.is_none() {
        None
    } else if end.is_none() {
        // 1..
        Some(RightInf(start.unwrap()))
    } else if start.is_none() {
        // ..1
        Some(LeftInf(end.unwrap()))
    } else {
        Some(Both(start.unwrap(), end.unwrap()))
    }
}

#[cfg(test)]
mod test {
    use super::FieldRange::*;
    #[test]
    fn test_parse_range() {
        assert_eq!(super::parse_range("1"), Some(Single(1)));
        assert_eq!(super::parse_range("-1"), Some(Single(-1)));

        assert_eq!(super::parse_range("1.."), Some(RightInf(1)));
        assert_eq!(super::parse_range("-1.."), Some(RightInf(-1)));

        assert_eq!(super::parse_range("..1"), Some(LeftInf(1)));
        assert_eq!(super::parse_range("..-1"), Some(LeftInf(-1)));

        assert_eq!(super::parse_range("1..3"), Some(Both(1, 3)));
        assert_eq!(super::parse_range("-1..-3"), Some(Both(-1, -3)));

        assert_eq!(super::parse_range(".."), Some(RightInf(0)));
        assert_eq!(super::parse_range("a.."), None);
        assert_eq!(super::parse_range("..b"), None);
        assert_eq!(super::parse_range("a..b"), None);
    }
}
<|endoftext|>


<commit_before><commit_msg>fn remove_unreachable_states<commit_after><|endoftext|>