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

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

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

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

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

mod error;
use self::error::FilterError;
pub use self::error::FilterResolveError;

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

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

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

pub struct FilterSpec {
    user_space_filter: UserSpaceFilter,
    primitives: Vec<UserSpacePrimitive>,
}

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

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

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

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

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

    params: PrimitiveParams,
}

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

enum_default!(Input, Input::Unspecified);

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

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

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

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

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

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

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

        Ok((input_1, input_2))
    }

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

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

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

pub fn extract_filter_from_filter_node(
    filter_node: &Node,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &DrawingCtx,
) -> Result<FilterSpec, FilterResolveError> {
    let filter_node = &*filter_node;
    assert!(is_element_of_type!(filter_node, Filter));

    let filter_element = filter_node.borrow_element();

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

        let filter = borrow_element_as!(filter_node, Filter);

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

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

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

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

            let primitive_values = elt.get_computed_values();

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

            effect
                .resolve(acquired_nodes, &primitive_node)
                .map_err(|e| {
                    rsvg_log!(
                        "(filter primitive {} returned an error: {})",
                        primitive_name,
                        e
                    );
                    e
                })
                .map(|primitive| {
                    primitive.into_user_space(
                        user_space_filter.primitive_units,
                        primitive_values,
                        draw_ctx,
                    )
                })
        })
        .collect::<Result<Vec<UserSpacePrimitive>, FilterResolveError>>()?;

    Ok(FilterSpec {
        user_space_filter,
        primitives,
    })
}

/// Applies a filter and returns the resulting surface.
pub fn render(
    filter: &FilterSpec,
    stroke_paint_source: Rc<UserSpacePaintSource>,
    fill_paint_source: Rc<UserSpacePaintSource>,
    source_surface: SharedImageSurface,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
    transform: Transform,
    node_bbox: BoundingBox,
) -> Result<SharedImageSurface, RenderingError> {
    FilterContext::new(
        &filter.user_space_filter,
        stroke_paint_source,
        fill_paint_source,
        &source_surface,
        transform,
        node_bbox,
    )
    .and_then(|mut filter_ctx| {
        for user_space_primitive in &filter.primitives {
            let start = Instant::now();

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

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

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

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

        Ok(filter_ctx.into_output()?)
    })
    .or_else(|err| match err {
        FilterError::CairoError(status) => {
            // Exit early on Cairo errors
            Err(RenderingError::from(status))
        }

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

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

    let bounds_builder = primitive.get_bounds(ctx);

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

impl From<ColorInterpolationFilters> for SurfaceType {
    fn from(c: ColorInterpolationFilters) -> Self {
        match c {
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
            _ => SurfaceType::SRgb,
        }
    }
}
<commit_msg>ResolvedPrimitive.into_user_space - take the ViewParams, not the whole DrawingCtx<commit_after>//! Entry point for the CSS filters infrastructure.

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

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

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

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

mod error;
use self::error::FilterError;
pub use self::error::FilterResolveError;

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

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

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

pub struct FilterSpec {
    user_space_filter: UserSpaceFilter,
    primitives: Vec<UserSpacePrimitive>,
}

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

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

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

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

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

    params: PrimitiveParams,
}

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

enum_default!(Input, Input::Unspecified);

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

impl ResolvedPrimitive {
    fn into_user_space(self, values: &ComputedValues, params: &ViewParams) -> UserSpacePrimitive {
        let x = self.primitive.x.map(|l| l.normalize(values, &params));
        let y = self.primitive.y.map(|l| l.normalize(values, &params));
        let width = self.primitive.width.map(|l| l.normalize(values, &params));
        let height = self.primitive.height.map(|l| l.normalize(values, &params));

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

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

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

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

        Ok((input_1, input_2))
    }

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

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

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

pub fn extract_filter_from_filter_node(
    filter_node: &Node,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &DrawingCtx,
) -> Result<FilterSpec, FilterResolveError> {
    let filter_node = &*filter_node;
    assert!(is_element_of_type!(filter_node, Filter));

    let filter_element = filter_node.borrow_element();

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

        let filter = borrow_element_as!(filter_node, Filter);

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

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

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

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

            let primitive_values = elt.get_computed_values();

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

            effect
                .resolve(acquired_nodes, &primitive_node)
                .map_err(|e| {
                    rsvg_log!(
                        "(filter primitive {} returned an error: {})",
                        primitive_name,
                        e
                    );
                    e
                })
                .map(|primitive| {
                    let params = draw_ctx.push_coord_units(user_space_filter.primitive_units);
                    primitive.into_user_space(primitive_values, &params)
                })
        })
        .collect::<Result<Vec<UserSpacePrimitive>, FilterResolveError>>()?;

    Ok(FilterSpec {
        user_space_filter,
        primitives,
    })
}

/// Applies a filter and returns the resulting surface.
pub fn render(
    filter: &FilterSpec,
    stroke_paint_source: Rc<UserSpacePaintSource>,
    fill_paint_source: Rc<UserSpacePaintSource>,
    source_surface: SharedImageSurface,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
    transform: Transform,
    node_bbox: BoundingBox,
) -> Result<SharedImageSurface, RenderingError> {
    FilterContext::new(
        &filter.user_space_filter,
        stroke_paint_source,
        fill_paint_source,
        &source_surface,
        transform,
        node_bbox,
    )
    .and_then(|mut filter_ctx| {
        for user_space_primitive in &filter.primitives {
            let start = Instant::now();

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

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

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

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

        Ok(filter_ctx.into_output()?)
    })
    .or_else(|err| match err {
        FilterError::CairoError(status) => {
            // Exit early on Cairo errors
            Err(RenderingError::from(status))
        }

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

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

    let bounds_builder = primitive.get_bounds(ctx);

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

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


<commit_before>use std::marker::PhantomData;
use std::collections::hash_map::{HashMap, Entry};
use gl;
use gl::types::*;
use context::Context;
use renderbuffer::{Renderbuffer, RenderbufferTarget};
use texture::{Texture, TextureType, ImageTargetType,
              Texture2d, Tx2dImageTarget};
use types::{BufferBits, GLError, GLFramebufferError};

pub struct Framebuffer {
    gl_id: GLuint
}

impl Framebuffer {
    pub unsafe fn from_gl(id: GLuint) -> Self {
        Framebuffer { gl_id: id }
    }

    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Framebuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteFramebuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

enum BuilderAttachment<'a> {
    Texture2d(&'a mut Texture2d, i32),
    Renderbuffer(&'a mut Renderbuffer)
}

pub struct FramebufferBuilder<'a> {
    gl: &'a mut Context,
    attachments: HashMap<FramebufferAttachment, BuilderAttachment<'a>>
}

impl<'a> FramebufferBuilder<'a> {
    fn new(gl: &'a mut Context) -> Self {
        FramebufferBuilder {
            gl: gl,
            attachments: HashMap::new()
        }
    }

    pub fn texture_2d(mut self,
                      attachment: FramebufferAttachment,
                      texture: &'a mut Texture2d,
                      level: i32)
        -> Self
    {
        let attached = BuilderAttachment::Texture2d(texture, level);
        match self.attachments.entry(attachment) {
            Entry::Occupied(mut e) => { e.insert(attached); },
            Entry::Vacant(e) => { e.insert(attached); }
        };

        self
    }

    pub fn renderbuffer(mut self,
                        attachment: FramebufferAttachment,
                        renderbuffer: &'a mut Renderbuffer)
        -> Self
    {
        let attached = BuilderAttachment::Renderbuffer(renderbuffer);
        match self.attachments.entry(attachment) {
            Entry::Occupied(mut e) => { e.insert(attached); },
            Entry::Vacant(e) => { e.insert(attached); }
        };

        self
    }

    pub fn try_unwrap(self) -> Result<Framebuffer, GLError> {
        let mut fbo = unsafe { self.gl.gen_framebuffer() };

        // TODO: Use `bind_framebuffer!` macro here
        let mut gl_fbo = self.gl.framebuffer.bind(&mut fbo);

        for (attachment, attached) in self.attachments.into_iter() {
            match attached {
                BuilderAttachment::Texture2d(texture, level) => {
                    gl_fbo.texture_2d(attachment,
                                      Tx2dImageTarget::Texture2d,
                                      texture,
                                      level);
                },
                BuilderAttachment::Renderbuffer(renderbuffer) => {
                    gl_fbo.renderbuffer(attachment, renderbuffer);
                }
            }
        }

        match gl_fbo.check_framebuffer_status() {
            Some(err) => { Err(err.into()) },
            None => { Ok(fbo) }
        }
    }

    pub fn unwrap(self) -> Framebuffer {
        self.try_unwrap().unwrap()
    }
}

impl Context {
    pub unsafe fn gen_framebuffer(&self) -> Framebuffer {
        let mut id : GLuint = 0;

        gl::GenFramebuffers(1, &mut id as *mut GLuint);
        dbg_gl_sanity_check! {
            GLError::InvalidValue => "`n` is negative",
            _ => "Unknown error"
        }

        Framebuffer {
            gl_id: id
        }
    }
}

gl_enum! {
    pub gl_enum FramebufferTarget {
        Framebuffer as FRAMEBUFFER = gl::FRAMEBUFFER
    }
}

gl_enum! {
    pub gl_enum FramebufferAttachment {
        ColorAttachment0 as COLOR_ATTACHMENT0 = gl::COLOR_ATTACHMENT0,
        DepthAttachment as DEPTH_ATTACHMENT = gl::DEPTH_ATTACHMENT,
        StencilAttachment as STENCIL_ATTACHMENT = gl::STENCIL_ATTACHMENT
    }
}

pub struct FramebufferBinding<'a> {
    phantom: PhantomData<&'a mut Framebuffer>
}

impl<'a> FramebufferBinding<'a> {
    fn target(&self) -> FramebufferTarget {
        FramebufferTarget::Framebuffer
    }

    pub fn check_framebuffer_status(&self) -> Option<GLFramebufferError> {
        unsafe {
            match gl::CheckFramebufferStatus(self.target().gl_enum()) {
                gl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => {
                    Some(GLFramebufferError::IncompleteAttachment)
                },
                // gl::FRAMEBUFFER_INCOMPLETE_DIMENSIONS => {
                //     Some(GLFramebufferError::IncompleteDimensions)
                // },
                gl::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => {
                    Some(GLFramebufferError::IncompleteMissingAttachment)
                },
                gl::FRAMEBUFFER_UNSUPPORTED => {
                    Some(GLFramebufferError::Unsupported)
                },
                _ => { None }
            }
        }
    }

    pub fn renderbuffer(&mut self,
                        attachment: FramebufferAttachment,
                        renderbuffer: &mut Renderbuffer)
    {
        let renderbuffer_target = RenderbufferTarget::Renderbuffer;
        unsafe {
            gl::FramebufferRenderbuffer(self.target().gl_enum(),
                                        attachment.gl_enum(),
                                        renderbuffer_target.gl_enum(),
                                        renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not a valid attachment point, or `renderbuffer` is not `GL_RENDERBUFFER` and `renderbuffer` is not 0",
                GLError::InvalidOperation => "Framebuffer 0 is bound, or `renderbuffer` is neither 0 nor the name of an existing renderbuffer object",
                _ => "Unknown error"
            }
        }
    }

    pub fn texture_2d<T, I>(&mut self,
                            attachment: FramebufferAttachment,
                            tex_target: I,
                            texture: &mut Texture<T>,
                            level: i32)
        where T: TextureType, I: Into<T::ImageTargetType>
    {
        debug_assert!(level == 0);

        unsafe {
            gl::FramebufferTexture2D(self.target().gl_enum(),
                                     attachment.gl_enum(),
                                     tex_target.into().gl_enum(),
                                     texture.gl_id(),
                                     level as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not an accepted attachment point, or `textarget` is not an accepted texture target and texture is not 0",
                GLError::InvalidValue => "`level` is not 0 and `texture` is not 0",
                GLError::InvalidOperation => "Framebuffer object 0 is bound, `texture` is neither 0 nor the name of an existing texture object, or `textarget` is not a valid target for `texture`",
                _ => "Unknown error"
            }
        }
    }

    pub fn clear(&mut self, buffers: BufferBits) {
        unsafe {
            gl::Clear(buffers.bits());
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`mask` includes a bit other than an allowed value",
                _ => "Unkown error"
            }
        }
    }
}

pub struct FramebufferBinder;
impl FramebufferBinder {
    pub unsafe fn current_binding(&mut self) -> FramebufferBinding {
        FramebufferBinding { phantom: PhantomData }
    }

    pub fn bind(&mut self, fbo: &mut Framebuffer) -> FramebufferBinding {
        let binding = FramebufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindFramebuffer(binding.target().gl_enum(), fbo.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<commit_msg>Add `Context.build_framebuffer()`<commit_after>use std::marker::PhantomData;
use std::collections::hash_map::{HashMap, Entry};
use gl;
use gl::types::*;
use context::Context;
use renderbuffer::{Renderbuffer, RenderbufferTarget};
use texture::{Texture, TextureType, ImageTargetType,
              Texture2d, Tx2dImageTarget};
use types::{BufferBits, GLError, GLFramebufferError};

pub struct Framebuffer {
    gl_id: GLuint
}

impl Framebuffer {
    pub unsafe fn from_gl(id: GLuint) -> Self {
        Framebuffer { gl_id: id }
    }

    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Framebuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteFramebuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

enum BuilderAttachment<'a> {
    Texture2d(&'a mut Texture2d, i32),
    Renderbuffer(&'a mut Renderbuffer)
}

pub struct FramebufferBuilder<'a> {
    gl: &'a mut Context,
    attachments: HashMap<FramebufferAttachment, BuilderAttachment<'a>>
}

impl<'a> FramebufferBuilder<'a> {
    fn new(gl: &'a mut Context) -> Self {
        FramebufferBuilder {
            gl: gl,
            attachments: HashMap::new()
        }
    }

    pub fn texture_2d(mut self,
                      attachment: FramebufferAttachment,
                      texture: &'a mut Texture2d,
                      level: i32)
        -> Self
    {
        let attached = BuilderAttachment::Texture2d(texture, level);
        match self.attachments.entry(attachment) {
            Entry::Occupied(mut e) => { e.insert(attached); },
            Entry::Vacant(e) => { e.insert(attached); }
        };

        self
    }

    pub fn renderbuffer(mut self,
                        attachment: FramebufferAttachment,
                        renderbuffer: &'a mut Renderbuffer)
        -> Self
    {
        let attached = BuilderAttachment::Renderbuffer(renderbuffer);
        match self.attachments.entry(attachment) {
            Entry::Occupied(mut e) => { e.insert(attached); },
            Entry::Vacant(e) => { e.insert(attached); }
        };

        self
    }

    pub fn try_unwrap(self) -> Result<Framebuffer, GLError> {
        let mut fbo = unsafe { self.gl.gen_framebuffer() };

        // TODO: Use `bind_framebuffer!` macro here
        let mut gl_fbo = self.gl.framebuffer.bind(&mut fbo);

        for (attachment, attached) in self.attachments.into_iter() {
            match attached {
                BuilderAttachment::Texture2d(texture, level) => {
                    gl_fbo.texture_2d(attachment,
                                      Tx2dImageTarget::Texture2d,
                                      texture,
                                      level);
                },
                BuilderAttachment::Renderbuffer(renderbuffer) => {
                    gl_fbo.renderbuffer(attachment, renderbuffer);
                }
            }
        }

        match gl_fbo.check_framebuffer_status() {
            Some(err) => { Err(err.into()) },
            None => { Ok(fbo) }
        }
    }

    pub fn unwrap(self) -> Framebuffer {
        self.try_unwrap().unwrap()
    }
}

impl Context {
    pub fn build_framebuffer(&mut self) -> FramebufferBuilder {
        FramebufferBuilder::new(self)
    }

    pub unsafe fn gen_framebuffer(&self) -> Framebuffer {
        let mut id : GLuint = 0;

        gl::GenFramebuffers(1, &mut id as *mut GLuint);
        dbg_gl_sanity_check! {
            GLError::InvalidValue => "`n` is negative",
            _ => "Unknown error"
        }

        Framebuffer {
            gl_id: id
        }
    }
}

gl_enum! {
    pub gl_enum FramebufferTarget {
        Framebuffer as FRAMEBUFFER = gl::FRAMEBUFFER
    }
}

gl_enum! {
    pub gl_enum FramebufferAttachment {
        ColorAttachment0 as COLOR_ATTACHMENT0 = gl::COLOR_ATTACHMENT0,
        DepthAttachment as DEPTH_ATTACHMENT = gl::DEPTH_ATTACHMENT,
        StencilAttachment as STENCIL_ATTACHMENT = gl::STENCIL_ATTACHMENT
    }
}

pub struct FramebufferBinding<'a> {
    phantom: PhantomData<&'a mut Framebuffer>
}

impl<'a> FramebufferBinding<'a> {
    fn target(&self) -> FramebufferTarget {
        FramebufferTarget::Framebuffer
    }

    pub fn check_framebuffer_status(&self) -> Option<GLFramebufferError> {
        unsafe {
            match gl::CheckFramebufferStatus(self.target().gl_enum()) {
                gl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => {
                    Some(GLFramebufferError::IncompleteAttachment)
                },
                // gl::FRAMEBUFFER_INCOMPLETE_DIMENSIONS => {
                //     Some(GLFramebufferError::IncompleteDimensions)
                // },
                gl::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => {
                    Some(GLFramebufferError::IncompleteMissingAttachment)
                },
                gl::FRAMEBUFFER_UNSUPPORTED => {
                    Some(GLFramebufferError::Unsupported)
                },
                _ => { None }
            }
        }
    }

    pub fn renderbuffer(&mut self,
                        attachment: FramebufferAttachment,
                        renderbuffer: &mut Renderbuffer)
    {
        let renderbuffer_target = RenderbufferTarget::Renderbuffer;
        unsafe {
            gl::FramebufferRenderbuffer(self.target().gl_enum(),
                                        attachment.gl_enum(),
                                        renderbuffer_target.gl_enum(),
                                        renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not a valid attachment point, or `renderbuffer` is not `GL_RENDERBUFFER` and `renderbuffer` is not 0",
                GLError::InvalidOperation => "Framebuffer 0 is bound, or `renderbuffer` is neither 0 nor the name of an existing renderbuffer object",
                _ => "Unknown error"
            }
        }
    }

    pub fn texture_2d<T, I>(&mut self,
                            attachment: FramebufferAttachment,
                            tex_target: I,
                            texture: &mut Texture<T>,
                            level: i32)
        where T: TextureType, I: Into<T::ImageTargetType>
    {
        debug_assert!(level == 0);

        unsafe {
            gl::FramebufferTexture2D(self.target().gl_enum(),
                                     attachment.gl_enum(),
                                     tex_target.into().gl_enum(),
                                     texture.gl_id(),
                                     level as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`, `attachment` is not an accepted attachment point, or `textarget` is not an accepted texture target and texture is not 0",
                GLError::InvalidValue => "`level` is not 0 and `texture` is not 0",
                GLError::InvalidOperation => "Framebuffer object 0 is bound, `texture` is neither 0 nor the name of an existing texture object, or `textarget` is not a valid target for `texture`",
                _ => "Unknown error"
            }
        }
    }

    pub fn clear(&mut self, buffers: BufferBits) {
        unsafe {
            gl::Clear(buffers.bits());
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`mask` includes a bit other than an allowed value",
                _ => "Unkown error"
            }
        }
    }
}

pub struct FramebufferBinder;
impl FramebufferBinder {
    pub unsafe fn current_binding(&mut self) -> FramebufferBinding {
        FramebufferBinding { phantom: PhantomData }
    }

    pub fn bind(&mut self, fbo: &mut Framebuffer) -> FramebufferBinding {
        let binding = FramebufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindFramebuffer(binding.target().gl_enum(), fbo.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_FRAMEBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<|endoftext|>


<commit_before>// FIXME: add the support of transient objects

use any_cache::{Cache, HashCache};
use notify::{self, RecommendedWatcher, Watcher};
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;
use std::time::{Duration, Instant};

/// Class of types that can be loaded.
pub trait Load: Sized {
  /// Arguments passed at loading.
  type Args: Clone;

  /// Static string representing the name of the type (used in filesystem caching).
  const TY_STR: &'static str;

  fn load<P>(path: P, cache: &mut ResCache, args: Self::Args) -> Result<Self> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload: Load {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<T> Reload for T where T: Load<Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

pub type Result<T> = ::std::result::Result<T, LoadError>;

/// Resources are wrapped in this type.
#[derive(Debug)]
pub struct Res<T>(Rc<RefCell<T>>);

impl<T> Res<T> {
  pub fn new(resource: T) -> Self {
    Res(Rc::new(RefCell::new(resource)))
  }

  pub fn as_rc(&self) -> &Rc<RefCell<T>> {
    &self.0
  }
}

impl<T> Clone for Res<T> {
  fn clone(&self) -> Self {
    Res(self.0.clone())
  }
}

impl<T> Deref for Res<T> {
  type Target = RefCell<T>;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

impl<T> From<Rc<RefCell<T>>> for Res<T> {
  fn from(rc: Rc<RefCell<T>>) -> Self {
    Res(rc)
  }
}

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME_MS: u64 = 1000;

/// Resource cache. Responsible for caching resource.
pub struct ResCache {
  // contains all the typed-erased Rc<RefCell<T>>
  cache: HashCache<PathBuf>,
  // contains all metadata on resources
  metadata: HashMap<PathBuf, ResMetaData>,
  // vector of pair (path, timestamp) giving indication on resources to reload
  dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>>,
  #[allow(dead_code)]
  watcher_thread: thread::JoinHandle<()>
}

struct ResMetaData {
  on_reload: Box<Fn(&mut ResCache)>,
  last_update_instant: Instant
}

impl ResCache {
  /// Create a new cache.
  pub fn new<P>(root: P) -> Self where P: AsRef<Path> {
    let dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>> = Arc::new(Mutex::new(Vec::new()));
    let dirty_ = dirty.clone();

    let root = root.as_ref().to_owned();
    let (wsx, wrx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

    let join_handle = thread::spawn(move || {
      let _ = watcher.watch(root);

      for event in wrx.iter() {
        if let notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } = event {
          dirty_.lock().unwrap().push((path.clone(), Instant::now()));
        }
      }
    });

    ResCache {
      cache: HashCache::new(),
      metadata: HashMap::new(),
      dirty: dirty,
      watcher_thread: join_handle
    }
  }

  /// Inject a new resource in the cache.
  fn inject<T>(&mut self, path_buf: &PathBuf, resource: T, args: T::Args) -> Res<T> where T: 'static + Any + Reload {
    let res = Res(Rc::new(RefCell::new(resource)));
    let res_ = res.clone();

    let path_buf_ = path_buf.clone();

    // closure used to reload the object when needed
    let on_reload: Box<for<'a> Fn(&'a mut ResCache)> = Box::new(move |cache_| {
      match T::load(&path_buf_, cache_, args.clone()) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          *res_.borrow_mut() = new_resource;
          deb!("reloaded resource from {:?}", path_buf_);
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed:\n{:#?}", path_buf_, e);
        }
      }
    });

    let metadata = ResMetaData {
      on_reload: on_reload,
      last_update_instant: Instant::now()
    };

    // cache the resource and its meta data
    self.cache.save(path_buf.clone(), res.clone());
    self.metadata.insert(path_buf.clone(), metadata);

    res
  }

  /// Get a resource from the cache and return an error if loading failed.
  fn get_<T>(&mut self, key: &str, args: T::Args) -> Result<Res<T>> where T: 'static + Any + Reload {
    let path_str = format!("data/{}/{}", T::TY_STR, key);
    let path = Path::new(&path_str);
    let path_buf = path.to_owned();

    match self.cache.get::<Res<T>>(&path_buf).cloned() {
      Some(resource) => {
        deb!("cache hit for {} ({})", key, path_str);
        Ok(resource)
      },
      None => {
        deb!("cache miss for {} ({})", key, path_str);

        // specific loading
        if path.exists() {
          let resource = T::load(&path, self, args.clone())?;
          Ok(self.inject(&path_buf, resource, args))
        } else {
          Err(LoadError::FileNotFound(path_buf, String::new()))
        }
      }
    }
  }

  /// Get a resource from the cache.
  pub fn get<T>(&mut self, key: &str, args: T::Args) -> Option<Res<T>> where T: 'static + Any + Reload {
    match self.get_(key, args) {
      Ok(resource) => Some(resource),
      Err(e) => {
        err!("{:?}", e);
        None
      }
    }
  }

  /// Get a resource from the cache. If it fails, a proxy version is used, which will get replaced
  /// by the resource once it’s available.
  pub fn get_proxied<T, P>(&mut self, key: &str, args: T::Args, proxy: P) -> Option<Res<T>>
      where T: 'static + Any + Reload,
            P: FnOnce() -> T {
    match self.get_::<T>(key, args.clone()) {
      Ok(resource) => Some(resource),
      Err(e) => {

        let path_str = format!("data/{}/{}", T::TY_STR, key);
        let path = Path::new(&path_str);
        let path_buf = path.to_owned();

        warn!("proxied resource {} because: {:?}", key, e);

        Some(self.inject(&path_buf, proxy(), args))
      }
    }
  }

  /// Synchronize the cache by updating the resource that ought to.
  pub fn sync(&mut self) {
    let dirty = self.dirty.clone();
    let mut dirty_ = dirty.lock().unwrap();

    for &(ref path, ref instant) in dirty_.iter() {
      let mut metadata = self.metadata.remove(path).unwrap();

      if instant.duration_since(metadata.last_update_instant) >= Duration::from_millis(UPDATE_AWAIT_TIME_MS) {
        (metadata.on_reload)(self);
      }

      metadata.last_update_instant = *instant;
      self.metadata.insert(path.clone(), metadata);
    }

    dirty_.clear();
  }
}
<commit_msg>Better error message when resources fail.<commit_after>// FIXME: add the support of transient objects

use any_cache::{Cache, HashCache};
use notify::{self, RecommendedWatcher, Watcher};
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;
use std::time::{Duration, Instant};

/// Class of types that can be loaded.
pub trait Load: Sized {
  /// Arguments passed at loading.
  type Args: Clone;

  /// Static string representing the name of the type (used in filesystem caching).
  const TY_STR: &'static str;

  fn load<P>(path: P, cache: &mut ResCache, args: Self::Args) -> Result<Self> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload: Load {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<T> Reload for T where T: Load<Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

pub type Result<T> = ::std::result::Result<T, LoadError>;

/// Resources are wrapped in this type.
#[derive(Debug)]
pub struct Res<T>(Rc<RefCell<T>>);

impl<T> Res<T> {
  pub fn new(resource: T) -> Self {
    Res(Rc::new(RefCell::new(resource)))
  }

  pub fn as_rc(&self) -> &Rc<RefCell<T>> {
    &self.0
  }
}

impl<T> Clone for Res<T> {
  fn clone(&self) -> Self {
    Res(self.0.clone())
  }
}

impl<T> Deref for Res<T> {
  type Target = RefCell<T>;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

impl<T> From<Rc<RefCell<T>>> for Res<T> {
  fn from(rc: Rc<RefCell<T>>) -> Self {
    Res(rc)
  }
}

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME_MS: u64 = 1000;

/// Resource cache. Responsible for caching resource.
pub struct ResCache {
  // contains all the typed-erased Rc<RefCell<T>>
  cache: HashCache<PathBuf>,
  // contains all metadata on resources
  metadata: HashMap<PathBuf, ResMetaData>,
  // vector of pair (path, timestamp) giving indication on resources to reload
  dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>>,
  #[allow(dead_code)]
  watcher_thread: thread::JoinHandle<()>
}

struct ResMetaData {
  on_reload: Box<Fn(&mut ResCache)>,
  last_update_instant: Instant
}

impl ResCache {
  /// Create a new cache.
  pub fn new<P>(root: P) -> Self where P: AsRef<Path> {
    let dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>> = Arc::new(Mutex::new(Vec::new()));
    let dirty_ = dirty.clone();

    let root = root.as_ref().to_owned();
    let (wsx, wrx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

    let join_handle = thread::spawn(move || {
      let _ = watcher.watch(root);

      for event in wrx.iter() {
        if let notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } = event {
          dirty_.lock().unwrap().push((path.clone(), Instant::now()));
        }
      }
    });

    ResCache {
      cache: HashCache::new(),
      metadata: HashMap::new(),
      dirty: dirty,
      watcher_thread: join_handle
    }
  }

  /// Inject a new resource in the cache.
  fn inject<T>(&mut self, path_buf: &PathBuf, resource: T, args: T::Args) -> Res<T> where T: 'static + Any + Reload {
    let res = Res(Rc::new(RefCell::new(resource)));
    let res_ = res.clone();

    let path_buf_ = path_buf.clone();

    // closure used to reload the object when needed
    let on_reload: Box<for<'a> Fn(&'a mut ResCache)> = Box::new(move |cache_| {
      match T::load(&path_buf_, cache_, args.clone()) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          *res_.borrow_mut() = new_resource;
          deb!("reloaded resource from {:?}", path_buf_);
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed:\n{:#?}", path_buf_, e);
        }
      }
    });

    let metadata = ResMetaData {
      on_reload: on_reload,
      last_update_instant: Instant::now()
    };

    // cache the resource and its meta data
    self.cache.save(path_buf.clone(), res.clone());
    self.metadata.insert(path_buf.clone(), metadata);

    res
  }

  /// Get a resource from the cache and return an error if loading failed.
  fn get_<T>(&mut self, key: &str, args: T::Args) -> Result<Res<T>> where T: 'static + Any + Reload {
    let path_str = format!("data/{}/{}", T::TY_STR, key);
    let path = Path::new(&path_str);
    let path_buf = path.to_owned();

    match self.cache.get::<Res<T>>(&path_buf).cloned() {
      Some(resource) => {
        deb!("cache hit for {} ({})", key, path_str);
        Ok(resource)
      },
      None => {
        deb!("cache miss for {} ({})", key, path_str);

        // specific loading
        if path.exists() {
          let resource = T::load(&path, self, args.clone())?;
          Ok(self.inject(&path_buf, resource, args))
        } else {
          Err(LoadError::FileNotFound(path_buf, String::new()))
        }
      }
    }
  }

  /// Get a resource from the cache.
  pub fn get<T>(&mut self, key: &str, args: T::Args) -> Option<Res<T>> where T: 'static + Any + Reload {
    match self.get_(key, args) {
      Ok(resource) => Some(resource),
      Err(e) => {
        err!("cannot get resource {}: {:?}", key, e);
        None
      }
    }
  }

  /// Get a resource from the cache. If it fails, a proxy version is used, which will get replaced
  /// by the resource once it’s available.
  pub fn get_proxied<T, P>(&mut self, key: &str, args: T::Args, proxy: P) -> Option<Res<T>>
      where T: 'static + Any + Reload,
            P: FnOnce() -> T {
    match self.get_::<T>(key, args.clone()) {
      Ok(resource) => Some(resource),
      Err(e) => {
        let path_str = format!("data/{}/{}", T::TY_STR, key);
        let path = Path::new(&path_str);
        let path_buf = path.to_owned();

        warn!("proxied resource {} because: {:?}", key, e);

        Some(self.inject(&path_buf, proxy(), args))
      }
    }
  }

  /// Synchronize the cache by updating the resource that ought to.
  pub fn sync(&mut self) {
    let dirty = self.dirty.clone();
    let mut dirty_ = dirty.lock().unwrap();

    for &(ref path, ref instant) in dirty_.iter() {
      let mut metadata = self.metadata.remove(path).unwrap();

      if instant.duration_since(metadata.last_update_instant) >= Duration::from_millis(UPDATE_AWAIT_TIME_MS) {
        (metadata.on_reload)(self);
      }

      metadata.last_update_instant = *instant;
      self.metadata.insert(path.clone(), metadata);
    }

    dirty_.clear();
  }
}
<|endoftext|>


<commit_before>use std::io::{IoResult, File};
use std::io::util::copy;

//! An alias of the rust-http Response struct.

pub use Response = http::server::response::ResponseWriter;

pub trait ServeFile: Writer {
    fn serve_file(&mut self, &Path) -> IoResult<()>;
}

impl<'a> ServeFile for Response<'a> {
    fn serve_file(&mut self, path: &Path) -> IoResult<()> {
        let mut file = try!(File::open(path));
        copy(&mut file, self)
    }
}
<commit_msg>(doc) Documented ServeFile trait.<commit_after>//! An augmentation of the rust-http Response struct.

use std::io::{IoResult, File};
use std::io::util::copy;

pub use Response = http::server::response::ResponseWriter;

/// Allow file-serving
pub trait ServeFile: Writer {
    /// Serve the file located at `path`.
    ///
    /// This is usually a terminal process, and `Middleware` may want to
    /// call `Unwind` after a file is served. If the status should be
    /// anything other than `200`, the `Middleware` must set it, including in
    /// the case of an `Err`.
    ///
    /// `serve_file` will err out if the file does not exist, the process
    /// does not have correct permissions, or it has other issues in reading
    /// from the file. Middleware should handle this gracefully.
    fn serve_file(&mut self, &Path) -> IoResult<()>;
}

impl<'a> ServeFile for Response<'a> {
    fn serve_file(&mut self, path: &Path) -> IoResult<()> {
        let mut file = try!(File::open(path));
        copy(&mut file, self)
    }
}
<|endoftext|>


<commit_before>
use hyper::StatusCode;
use hyper::header::ByteRangeSpec;
use hyper::header::ByteRangeSpec::FromTo;
use hyper::header::Range::Bytes;
use hyper::header::{ContentLength, ContentRange, ContentRangeSpec};

use reqwest;
use reqwest::Client;
use reqwest::Error;
use reqwest::header;

use reader::Reader;
use buffer::Buffer;

use std::io::SeekFrom;
use std::cmp;
use std::time::Instant;

fn get_head(filename: &String) -> Result<reqwest::Response, Error> {
  if filename.contains("s3.amazonaws.com") {
    let range = vec![FromTo(0, 0)];

    let mut headers = header::Headers::new();
    headers.set(Bytes(range));
    let client = reqwest::Client::builder()
      .default_headers(headers)
      .build()
      .unwrap();

    client.get(filename).send()
  } else {
    let client = Client::new();
    client.head(filename).send()
  }
}

#[derive(Debug)]
struct ResponseData {
  body_data: Vec<u8>,
  file_size: u64
}

fn get_data(filename: &String, range: Vec<ByteRangeSpec>) -> Result<ResponseData, String> {
  let mut headers = header::Headers::new();
  headers.set(Bytes(range));
  let client = reqwest::Client::builder()
    .default_headers(headers)
    .build()
    .unwrap();

  let mut response = client.get(filename).send().unwrap();

  let status = response.status();

  if !(status == StatusCode::Ok || status == StatusCode::PartialContent) {
    println!("ERROR {:?}", response);
    return Err("bad response status".to_string());
  }

  let mut body: Vec<u8> = vec![];
  response.copy_to(&mut body).unwrap();

  let file_size =
    match get_content_range(&response) {
      Ok(length) => length.unwrap(),
      Err(_msg) => return Err("bad response header".to_string()),
    };

  Ok(ResponseData{
    body_data: body,
    file_size: file_size
  })
}

fn get_content_length(response: &reqwest::Response) -> Option<u64> {
  match response.headers().get::<ContentLength>() {
    Some(length) => Some(**length as u64),
    None => None
  }
}

fn get_content_range(response: &reqwest::Response) -> Result<Option<u64>, String> {
  match response.headers().get::<ContentRange>() {
    Some(content_range) => {
      match content_range.clone() {
        ContentRange(ContentRangeSpec::Bytes{
            range: _range,
            instance_length: length
          }) => {
          Ok(length)
        },
        ContentRange(ContentRangeSpec::Unregistered{unit: _unit, resp: _resp}) => {
          Err("Unregistered, actually unsupported".to_string())
        }
      }
    },
    None =>
      Err("Missing content_range".to_string()),
  }
}

#[derive(Debug)]
pub struct HttpReader {
  pub filename: String,
  pub file_size: Option<u64>,
  pub position: u64,
  pub buffer: Buffer
}

pub fn exists(filename: &String) -> bool {
  match get_head(filename) {
    Ok(resp) => {
      resp.status().is_success()
    },
    Err(_msg) => {
      false
    }
  }
}

fn get_data_range(position: u64, size: usize, max_end_position: Option<u64>) -> Vec<ByteRangeSpec> {
  let start = position;
  let end =
    match (position, size) {
      (0, 0) => 0,
      (_, _) => {
        match max_end_position {
          Some(max) => {
            let max_size = max - position;
            position + cmp::min((size - 1) as u64, max_size)
          },
          None => position + (size - 1) as u64,
        }
      },
    };

  vec![FromTo(start, end)]
}

fn load_data(reader: &mut HttpReader, size: usize) -> Result<Option<Vec<u8>>, String> {
  let start = Instant::now();
  info!("make HTTP request with request {:?} bytes", size);

  let position =
    match reader.buffer.size {
      Some(_) => reader.buffer.position,
      None => reader.position,
    };

  match reader.file_size {
    Some(size) => {
      if position >= size {
        return Ok(None)
      }
    },
    None => (),
  };

  let range = get_data_range(position, size, reader.buffer.max_end_position);
  let response = get_data(&reader.filename, range).map_err(|e| e.to_string()).unwrap();

  let elapsed = start.elapsed();
  if elapsed.as_secs() > 0 {
    println!("WARNING: Request duration {} seconds", elapsed.as_secs());
  }

  let new_position = position + response.body_data.len() as u64;
  match reader.buffer.size {
    Some(_) => {
      reader.buffer.position = new_position;
    },
    None => {
      reader.position = new_position;
    }
  };
  Ok(Some(response.body_data))
}

impl Reader for HttpReader {
  fn open(filename: &String) -> HttpReader {
    match get_head(filename) {
      Err(msg) => panic!(msg.to_string()),
      Ok(response) => {
        let content_length = get_content_length(&response);

        HttpReader {
          filename: filename.to_string(),
          file_size: content_length,
          position: 0,
          buffer: Buffer {
            size: None,
            position: 0,
            max_end_position: None,
            buffer: vec![]
          }
        }
      }
    }
  }

  fn get_position(&mut self) -> Result<u64, String> {
    Ok(self.position)
  }

  fn get_cache_size(&self) -> Option<usize> {
    self.buffer.size
  }

  fn set_cache_size(&mut self, cache_size: Option<usize>) {
    self.buffer.size = cache_size;
  }

  fn get_max_end_position(&self) -> Option<u64> {
    self.buffer.max_end_position
  }

  fn set_max_end_position(&mut self, max_end_position: Option<u64>) {
    self.buffer.max_end_position = max_end_position;
  }

  fn get_size(&mut self) -> Result<u64, String> {
    match self.file_size {
        Some(length) => Ok(length),
        None => Err("No length detected".to_string()),
    }
  }

  fn read(&mut self, size: usize) -> Result<Vec<u8>, String> {
    if self.buffer.get_cached_size() >= size {
      self.position += size as u64;
      Ok(self.buffer.get_data(size))
    } else {
      match self.buffer.size {
        Some(buffer_size) => {
          match load_data(self, buffer_size) {
            Err(msg) => Err(msg),
            Ok(some_data) => {
              match some_data {
                Some(data) => {
                  self.buffer.append_data(&data.to_vec());
                  self.position += size as u64;
                  Ok(self.buffer.get_data(size))
                },
                None => Ok(Vec::new()),
              }
            }
          }
        },
        None => {
          match load_data(self, size) {
            Err(msg) => Err(msg),
            Ok(some_data) => {
              match some_data {
                Some(data) => {
                  // println!("{:?} vs {:?}", data.len(), size);
                  match data.len() >= size {
                    true => {
                      Ok(data)
                    },
                    false => Ok(Vec::new()),
                  }
                },
                None => Ok(Vec::new()),
              }
            }
          }
        }
      }
    }
  }

  fn seek(&mut self, seek: SeekFrom) -> Result<u64, String> {
    match seek {
      SeekFrom::Current(offset) => {
        self.position = self.position + offset as u64;
        if self.buffer.size.is_some() {
          if offset > 0 && self.buffer.get_cached_size() > offset as usize {
            let _skiped_data = self.buffer.get_data(offset as usize);
          } else {
            self.buffer.reset();
          }
        }
      },
      SeekFrom::Start(offset) => {
        self.buffer.reset();
        self.position = offset;
        if self.buffer.size.is_some() {
          self.buffer.position = self.position;
        }
      },
      SeekFrom::End(offset) => {
        self.buffer.reset();
        match self.file_size {
          Some(size) => {
            self.position = size - offset as u64;
            if self.buffer.size.is_some() {
              self.buffer.position = self.position;
            }
          },
          None => return Err("Missing file size".to_string())
        }
      }
    }
    Ok(self.position)
  }
}
<commit_msg>set content length from get_content_range if exists<commit_after>
use hyper::StatusCode;
use hyper::header::ByteRangeSpec;
use hyper::header::ByteRangeSpec::FromTo;
use hyper::header::Range::Bytes;
use hyper::header::{ContentLength, ContentRange, ContentRangeSpec};

use reqwest;
use reqwest::Client;
use reqwest::Error;
use reqwest::header;

use reader::Reader;
use buffer::Buffer;

use std::io::SeekFrom;
use std::cmp;
use std::time::Instant;

fn get_head(filename: &String) -> Result<reqwest::Response, Error> {
  if filename.contains("s3.amazonaws.com") {
    let range = vec![FromTo(0, 0)];

    let mut headers = header::Headers::new();
    headers.set(Bytes(range));
    let client = reqwest::Client::builder()
      .default_headers(headers)
      .build()
      .unwrap();

    client.get(filename).send()
  } else {
    let client = Client::new();
    client.head(filename).send()
  }
}

#[derive(Debug)]
struct ResponseData {
  body_data: Vec<u8>,
  file_size: u64
}

fn get_data(filename: &String, range: Vec<ByteRangeSpec>) -> Result<ResponseData, String> {
  let mut headers = header::Headers::new();
  headers.set(Bytes(range));
  let client = reqwest::Client::builder()
    .default_headers(headers)
    .build()
    .unwrap();

  let mut response = client.get(filename).send().unwrap();

  let status = response.status();

  if !(status == StatusCode::Ok || status == StatusCode::PartialContent) {
    println!("ERROR {:?}", response);
    return Err("bad response status".to_string());
  }

  let mut body: Vec<u8> = vec![];
  response.copy_to(&mut body).unwrap();

  let file_size =
    match get_content_range(&response) {
      Ok(length) => length.unwrap(),
      Err(_msg) => return Err("bad response header".to_string()),
    };

  Ok(ResponseData{
    body_data: body,
    file_size: file_size
  })
}

fn get_content_length(response: &reqwest::Response) -> Option<u64> {
  match response.headers().get::<ContentLength>() {
    Some(length) => Some(**length as u64),
    None => None
  }
}

fn get_content_range(response: &reqwest::Response) -> Result<Option<u64>, String> {
  match response.headers().get::<ContentRange>() {
    Some(content_range) => {
      match content_range.clone() {
        ContentRange(ContentRangeSpec::Bytes{
            range: _range,
            instance_length: length
          }) => {
          Ok(length)
        },
        ContentRange(ContentRangeSpec::Unregistered{unit: _unit, resp: _resp}) => {
          Err("Unregistered, actually unsupported".to_string())
        }
      }
    },
    None =>
      Err("Missing content_range".to_string()),
  }
}

#[derive(Debug)]
pub struct HttpReader {
  pub filename: String,
  pub file_size: Option<u64>,
  pub position: u64,
  pub buffer: Buffer
}

pub fn exists(filename: &String) -> bool {
  match get_head(filename) {
    Ok(resp) => {
      resp.status().is_success()
    },
    Err(_msg) => {
      false
    }
  }
}

fn get_data_range(position: u64, size: usize, max_end_position: Option<u64>) -> Vec<ByteRangeSpec> {
  let start = position;
  let end =
    match (position, size) {
      (0, 0) => 0,
      (_, _) => {
        match max_end_position {
          Some(max) => {
            let max_size = max - position;
            position + cmp::min((size - 1) as u64, max_size)
          },
          None => position + (size - 1) as u64,
        }
      },
    };

  vec![FromTo(start, end)]
}

fn load_data(reader: &mut HttpReader, size: usize) -> Result<Option<Vec<u8>>, String> {
  let start = Instant::now();
  info!("make HTTP request with request {:?} bytes", size);

  let position =
    match reader.buffer.size {
      Some(_) => reader.buffer.position,
      None => reader.position,
    };

  match reader.file_size {
    Some(size) => {
      if position >= size {
        return Ok(None)
      }
    },
    None => (),
  };

  let range = get_data_range(position, size, reader.buffer.max_end_position);
  let response = get_data(&reader.filename, range).map_err(|e| e.to_string()).unwrap();

  let elapsed = start.elapsed();
  if elapsed.as_secs() > 0 {
    println!("WARNING: Request duration {} seconds", elapsed.as_secs());
  }

  let new_position = position + response.body_data.len() as u64;
  match reader.buffer.size {
    Some(_) => {
      reader.buffer.position = new_position;
    },
    None => {
      reader.position = new_position;
    }
  };
  Ok(Some(response.body_data))
}

impl Reader for HttpReader {
  fn open(filename: &String) -> HttpReader {
    match get_head(filename) {
      Err(msg) => panic!(msg.to_string()),
      Ok(response) => {
        let content_length =
          match get_content_range(&response) {
            Ok(length) => length,
            _ => get_content_length(&response),
          };

        HttpReader {
          filename: filename.to_string(),
          file_size: content_length,
          position: 0,
          buffer: Buffer {
            size: None,
            position: 0,
            max_end_position: None,
            buffer: vec![]
          }
        }
      }
    }
  }

  fn get_position(&mut self) -> Result<u64, String> {
    Ok(self.position)
  }

  fn get_cache_size(&self) -> Option<usize> {
    self.buffer.size
  }

  fn set_cache_size(&mut self, cache_size: Option<usize>) {
    self.buffer.size = cache_size;
  }

  fn get_max_end_position(&self) -> Option<u64> {
    self.buffer.max_end_position
  }

  fn set_max_end_position(&mut self, max_end_position: Option<u64>) {
    self.buffer.max_end_position = max_end_position;
  }

  fn get_size(&mut self) -> Result<u64, String> {
    match self.file_size {
        Some(length) => Ok(length),
        None => Err("No length detected".to_string()),
    }
  }

  fn read(&mut self, size: usize) -> Result<Vec<u8>, String> {
    if self.buffer.get_cached_size() >= size {
      self.position += size as u64;
      Ok(self.buffer.get_data(size))
    } else {
      match self.buffer.size {
        Some(buffer_size) => {
          match load_data(self, buffer_size) {
            Err(msg) => Err(msg),
            Ok(some_data) => {
              match some_data {
                Some(data) => {
                  self.buffer.append_data(&data.to_vec());
                  self.position += size as u64;
                  Ok(self.buffer.get_data(size))
                },
                None => Ok(Vec::new()),
              }
            }
          }
        },
        None => {
          match load_data(self, size) {
            Err(msg) => Err(msg),
            Ok(some_data) => {
              match some_data {
                Some(data) => {
                  // println!("{:?} vs {:?}", data.len(), size);
                  match data.len() >= size {
                    true => {
                      Ok(data)
                    },
                    false => Ok(Vec::new()),
                  }
                },
                None => Ok(Vec::new()),
              }
            }
          }
        }
      }
    }
  }

  fn seek(&mut self, seek: SeekFrom) -> Result<u64, String> {
    match seek {
      SeekFrom::Current(offset) => {
        self.position = self.position + offset as u64;
        if self.buffer.size.is_some() {
          if offset > 0 && self.buffer.get_cached_size() > offset as usize {
            let _skiped_data = self.buffer.get_data(offset as usize);
          } else {
            self.buffer.reset();
          }
        }
      },
      SeekFrom::Start(offset) => {
        self.buffer.reset();
        self.position = offset;
        if self.buffer.size.is_some() {
          self.buffer.position = self.position;
        }
      },
      SeekFrom::End(offset) => {
        self.buffer.reset();
        match self.file_size {
          Some(size) => {
            self.position = size - offset as u64;
            if self.buffer.size.is_some() {
              self.buffer.position = self.position;
            }
          },
          None => return Err("Missing file size".to_string())
        }
      }
    }
    Ok(self.position)
  }
}
<|endoftext|>


<commit_before>#![crate_name = "sdl2"]
#![crate_type = "lib"]

#![feature(slicing_syntax, unsafe_destructor, optin_builtin_traits, std_misc, io, hash, core, collections, path)]

extern crate libc;
extern crate collections;
#[macro_use]
extern crate bitflags;
extern crate "sdl2-sys" as sys;

pub use sdl::*;

pub mod keycode;
pub mod scancode;

pub mod clipboard;
pub mod cpuinfo;
#[macro_use] pub mod macros;
pub mod event;
pub mod filesystem;
pub mod gesture;
pub mod touch;
pub mod joystick;
pub mod controller;
pub mod haptic;
pub mod keyboard;
pub mod mouse;
pub mod rect;
pub mod surface;
pub mod pixels;
pub mod video;
pub mod timer;
pub mod render;
pub mod rwops;
pub mod sdl;
pub mod audio;
pub mod version;
pub mod messagebox;
<commit_msg>Feature for slicing syntax no longer required<commit_after>#![crate_name = "sdl2"]
#![crate_type = "lib"]

#![feature(unsafe_destructor, optin_builtin_traits, std_misc, io, hash, core, collections, path)]

extern crate libc;
extern crate collections;
#[macro_use]
extern crate bitflags;
extern crate "sdl2-sys" as sys;

pub use sdl::*;

pub mod keycode;
pub mod scancode;

pub mod clipboard;
pub mod cpuinfo;
#[macro_use] pub mod macros;
pub mod event;
pub mod filesystem;
pub mod gesture;
pub mod touch;
pub mod joystick;
pub mod controller;
pub mod haptic;
pub mod keyboard;
pub mod mouse;
pub mod rect;
pub mod surface;
pub mod pixels;
pub mod video;
pub mod timer;
pub mod render;
pub mod rwops;
pub mod sdl;
pub mod audio;
pub mod version;
pub mod messagebox;
<|endoftext|>


<commit_before>use crate::builtins::blob::{Blob, RawBlob};
use crate::indices::{FuncIdx, GlobalIdx, MemIdx, TableIdx, TypeIdx};
use crate::instructions::Expression;
use crate::types::{FuncType, GlobalType, MemType, TableType, ValueType};
use crate::{
    wasmbin_discriminants, DecodeError, Wasmbin, WasmbinCountable, WasmbinDecode,
    WasmbinDecodeWithDiscriminant, WasmbinEncode,
};
use arbitrary::Arbitrary;
use custom_debug::CustomDebug;

#[derive(Wasmbin, CustomDebug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct CustomSection {
    pub name: String,

    #[debug(with = "custom_debug::hexbuf_str")]
    pub data: Vec<u8>,
}

#[wasmbin_discriminants]
#[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
#[repr(u8)]
pub enum ImportDesc {
    Func(TypeIdx) = 0x00,
    Table(TableType) = 0x01,
    Mem(MemType) = 0x02,
    Global(GlobalType) = 0x03,
}

#[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct ImportPath {
    pub module: String,
    pub name: String,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Import {
    pub path: ImportPath,
    pub desc: ImportDesc,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Global {
    pub ty: GlobalType,
    pub init: Expression,
}

#[wasmbin_discriminants]
#[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
#[repr(u8)]
pub enum ExportDesc {
    Func(FuncIdx) = 0x00,
    Table(TableIdx) = 0x01,
    Mem(MemIdx) = 0x02,
    Global(GlobalIdx) = 0x03,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Export {
    pub name: String,
    pub desc: ExportDesc,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Element {
    pub table: TableIdx,
    pub offset: Expression,
    pub init: Vec<FuncIdx>,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Locals {
    pub repeat: u32,
    pub ty: ValueType,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Default, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Func {
    pub locals: Vec<Locals>,
    pub body: Expression,
}

#[derive(Wasmbin, WasmbinCountable, CustomDebug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Data {
    pub memory: MemIdx,
    pub offset: Expression,
    #[debug(with = "custom_debug::hexbuf_str")]
    pub init: RawBlob,
}

pub trait Payload: WasmbinEncode + WasmbinDecode + Into<Section> {
    const KIND: Kind;

    fn try_from_ref(section: &Section) -> Option<&Blob<Self>>;
    fn try_from_mut(section: &mut Section) -> Option<&mut Blob<Self>>;
    fn try_from(section: Section) -> Result<Blob<Self>, Section>;
}

pub trait StdPayload: Payload {}

macro_rules! define_sections {
    ($($name:ident($ty:ty) = $disc:literal,)*) => {
        pub mod payload {
            $(pub type $name = $ty;)*
        }

        #[wasmbin_discriminants]
        #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
        #[repr(u8)]
        pub enum Section {
            $($name(Blob<payload::$name>) = $disc,)*
        }

        #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
        #[repr(u8)]
        pub enum Kind {
            $($name = $disc,)*
        }

        $(
            impl From<Blob<payload::$name>> for Section {
                fn from(value: Blob<payload::$name>) -> Self {
                    Section::$name(value)
                }
            }

            impl From<payload::$name> for Section {
                fn from(value: payload::$name) -> Self {
                    Section::$name(Blob::from(value))
                }
            }

            impl Payload for payload::$name {
                const KIND: Kind = Kind::$name;

                fn try_from_ref(section: &Section) -> Option<&Blob<Self>> {
                    match section {
                        Section::$name(res) => Some(res),
                        _ => None,
                    }
                }

                fn try_from_mut(section: &mut Section) -> Option<&mut Blob<Self>> {
                    match section {
                        Section::$name(res) => Some(res),
                        _ => None,
                    }
                }

                fn try_from(section: Section) -> Result<Blob<Self>, Section> {
                    match section {
                        Section::$name(res) => Ok(res),
                        _ => Err(section),
                    }
                }
            }
        )*

        impl Section {
            pub fn kind(&self) -> Kind {
                match self {
                    $(Section::$name(_) => Kind::$name,)*
                }
            }

            pub fn try_as<T: Payload>(&self) -> Option<&Blob<T>> {
                T::try_from_ref(self)
            }

            pub fn try_as_mut<T: Payload>(&mut self) -> Option<&mut Blob<T>> {
                T::try_from_mut(self)
            }
        }

        define_sections!(@std $($name)*);
    };

    (@std $ignore_custom:ident $($name:ident)*) => {
        $(impl StdPayload for payload::$name {})*
    };
}

define_sections! {
    Custom(super::CustomSection) = 0,
    Type(Vec<super::FuncType>) = 1,
    Import(Vec<super::Import>) = 2,
    Function(Vec<super::TypeIdx>) = 3,
    Table(Vec<super::TableType>) = 4,
    Memory(Vec<super::MemType>) = 5,
    Global(Vec<super::Global>) = 6,
    Export(Vec<super::Export>) = 7,
    Start(super::FuncIdx) = 8,
    Element(Vec<super::Element>) = 9,
    Code(Vec<super::Blob<super::Func>>) = 10,
    Data(Vec<super::Data>) = 11,
}

impl WasmbinEncode for [Section] {
    fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
        for section in self {
            section.encode(w)?;
        }
        Ok(())
    }
}

impl WasmbinDecode for Vec<Section> {
    fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
        let mut sections = Vec::new();
        loop {
            match u8::decode(r) {
                Err(DecodeError::Io(err)) if err.kind() == std::io::ErrorKind::UnexpectedEof => {
                    return Ok(sections);
                }
                Err(err) => {
                    return Err(err);
                }
                Ok(discriminant) => {
                    sections.push(Section::decode_with_discriminant(discriminant, r)?);
                }
            }
        }
    }
}
<commit_msg>Add known custom section support<commit_after>use crate::builtins::blob::{Blob, RawBlob};
use crate::builtins::lazy::Lazy;
use crate::indices::{FuncIdx, GlobalIdx, LocalIdx, MemIdx, TableIdx, TypeIdx};
use crate::instructions::Expression;
use crate::types::{FuncType, GlobalType, MemType, TableType, ValueType};
use crate::{
    wasmbin_discriminants, DecodeError, Wasmbin, WasmbinCountable, WasmbinDecode,
    WasmbinDecodeWithDiscriminant, WasmbinEncode,
};
use arbitrary::Arbitrary;
use custom_debug::CustomDebug;

#[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct ModuleNameSubSection {
    pub name: String,
}

#[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct NameAssoc<I, V> {
    pub index: I,
    pub value: V,
}

impl<I, V> WasmbinCountable for NameAssoc<I, V> {}

#[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct NameMap<I, V> {
    pub items: Vec<NameAssoc<I, V>>,
}

#[wasmbin_discriminants]
#[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
#[repr(u8)]
pub enum NameSubSection {
    Module(Blob<String>) = 0,
    Func(Blob<NameMap<FuncIdx, String>>) = 1,
    Local(Blob<NameMap<FuncIdx, NameMap<LocalIdx, String>>>) = 2,
}

impl WasmbinEncode for [NameSubSection] {
    fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
        for sub in self {
            sub.encode(w)?;
        }
        Ok(())
    }
}

impl WasmbinDecode for Vec<NameSubSection> {
    fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
        let mut sub = Vec::new();
        loop {
            match u8::decode(r) {
                Err(DecodeError::Io(err)) if err.kind() == std::io::ErrorKind::UnexpectedEof => {
                    return Ok(sub);
                }
                Err(err) => {
                    return Err(err);
                }
                Ok(discriminant) => {
                    sub.push(NameSubSection::decode_with_discriminant(discriminant, r)?);
                }
            }
        }
    }
}

macro_rules! define_custom_sections {
    ($($name:ident($ty:ty) = $disc:literal,)*) => {
        #[derive(CustomDebug, Arbitrary, PartialEq, Eq, Hash, Clone)]
        pub enum CustomSection {
            $($name(Lazy<$ty>),)*

            Other {
                name: String,

                #[debug(with = "custom_debug::hexbuf_str")]
                data: Vec<u8>,
            },
        }

        impl WasmbinEncode for CustomSection {
            fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
                match self {
                    $(CustomSection::$name(data) => {
                        $disc.encode(w)?;
                        data.encode(w)
                    })*
                    CustomSection::Other { name, data } => {
                        name.encode(w)?;
                        data.encode(w)
                    }
                }
            }
        }

        impl WasmbinDecode for CustomSection {
            fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
                let name = String::decode(r)?;
                Ok(match name.as_str() {
                    $($disc => CustomSection::$name(Lazy::decode(r)?))*,
                    _ => CustomSection::Other {
                        name,
                        data: Vec::decode(r)?,
                    },
                })
            }
        }
    };
}

define_custom_sections! {
    Name(Vec<NameSubSection>) = "name",
}

#[wasmbin_discriminants]
#[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
#[repr(u8)]
pub enum ImportDesc {
    Func(TypeIdx) = 0x00,
    Table(TableType) = 0x01,
    Mem(MemType) = 0x02,
    Global(GlobalType) = 0x03,
}

#[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct ImportPath {
    pub module: String,
    pub name: String,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Import {
    pub path: ImportPath,
    pub desc: ImportDesc,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Global {
    pub ty: GlobalType,
    pub init: Expression,
}

#[wasmbin_discriminants]
#[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
#[repr(u8)]
pub enum ExportDesc {
    Func(FuncIdx) = 0x00,
    Table(TableIdx) = 0x01,
    Mem(MemIdx) = 0x02,
    Global(GlobalIdx) = 0x03,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Export {
    pub name: String,
    pub desc: ExportDesc,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Element {
    pub table: TableIdx,
    pub offset: Expression,
    pub init: Vec<FuncIdx>,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Locals {
    pub repeat: u32,
    pub ty: ValueType,
}

#[derive(Wasmbin, WasmbinCountable, Debug, Default, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Func {
    pub locals: Vec<Locals>,
    pub body: Expression,
}

#[derive(Wasmbin, WasmbinCountable, CustomDebug, Arbitrary, PartialEq, Eq, Hash, Clone)]
pub struct Data {
    pub memory: MemIdx,
    pub offset: Expression,
    #[debug(with = "custom_debug::hexbuf_str")]
    pub init: RawBlob,
}

pub trait Payload: WasmbinEncode + WasmbinDecode + Into<Section> {
    const KIND: Kind;

    fn try_from_ref(section: &Section) -> Option<&Blob<Self>>;
    fn try_from_mut(section: &mut Section) -> Option<&mut Blob<Self>>;
    fn try_from(section: Section) -> Result<Blob<Self>, Section>;
}

pub trait StdPayload: Payload {}

macro_rules! define_sections {
    ($($name:ident($ty:ty) = $disc:literal,)*) => {
        pub mod payload {
            $(pub type $name = $ty;)*
        }

        #[wasmbin_discriminants]
        #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone)]
        #[repr(u8)]
        pub enum Section {
            $($name(Blob<payload::$name>) = $disc,)*
        }

        #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
        #[repr(u8)]
        pub enum Kind {
            $($name = $disc,)*
        }

        $(
            impl From<Blob<payload::$name>> for Section {
                fn from(value: Blob<payload::$name>) -> Self {
                    Section::$name(value)
                }
            }

            impl From<payload::$name> for Section {
                fn from(value: payload::$name) -> Self {
                    Section::$name(Blob::from(value))
                }
            }

            impl Payload for payload::$name {
                const KIND: Kind = Kind::$name;

                fn try_from_ref(section: &Section) -> Option<&Blob<Self>> {
                    match section {
                        Section::$name(res) => Some(res),
                        _ => None,
                    }
                }

                fn try_from_mut(section: &mut Section) -> Option<&mut Blob<Self>> {
                    match section {
                        Section::$name(res) => Some(res),
                        _ => None,
                    }
                }

                fn try_from(section: Section) -> Result<Blob<Self>, Section> {
                    match section {
                        Section::$name(res) => Ok(res),
                        _ => Err(section),
                    }
                }
            }
        )*

        impl Section {
            pub fn kind(&self) -> Kind {
                match self {
                    $(Section::$name(_) => Kind::$name,)*
                }
            }

            pub fn try_as<T: Payload>(&self) -> Option<&Blob<T>> {
                T::try_from_ref(self)
            }

            pub fn try_as_mut<T: Payload>(&mut self) -> Option<&mut Blob<T>> {
                T::try_from_mut(self)
            }
        }

        define_sections!(@std $($name)*);
    };

    (@std $ignore_custom:ident $($name:ident)*) => {
        $(impl StdPayload for payload::$name {})*
    };
}

define_sections! {
    Custom(super::CustomSection) = 0,
    Type(Vec<super::FuncType>) = 1,
    Import(Vec<super::Import>) = 2,
    Function(Vec<super::TypeIdx>) = 3,
    Table(Vec<super::TableType>) = 4,
    Memory(Vec<super::MemType>) = 5,
    Global(Vec<super::Global>) = 6,
    Export(Vec<super::Export>) = 7,
    Start(super::FuncIdx) = 8,
    Element(Vec<super::Element>) = 9,
    Code(Vec<super::Blob<super::Func>>) = 10,
    Data(Vec<super::Data>) = 11,
}

impl WasmbinEncode for [Section] {
    fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
        for section in self {
            section.encode(w)?;
        }
        Ok(())
    }
}

impl WasmbinDecode for Vec<Section> {
    fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
        let mut sections = Vec::new();
        loop {
            match u8::decode(r) {
                Err(DecodeError::Io(err)) if err.kind() == std::io::ErrorKind::UnexpectedEof => {
                    return Ok(sections);
                }
                Err(err) => {
                    return Err(err);
                }
                Ok(discriminant) => {
                    sections.push(Section::decode_with_discriminant(discriminant, r)?);
                }
            }
        }
    }
}
<|endoftext|>