<commit_before>use crate::{sys, Token};

use std::fmt;

/// A readiness event.
///
/// `Event` is a readiness state paired with a [`Token`]. It is returned by
/// [`Poll::poll`].
///
/// For more documentation on polling and events, see [`Poll`].
///
/// [`Poll::poll`]: crate::Poll::poll
/// [`Poll`]: crate::Poll
/// [`Token`]: crate::Token
#[repr(transparent)]
pub struct Event {
    inner: sys::Event,
}

impl Event {
    /// Returns the event's token.
    #[inline]
    pub fn token(&self) -> Token {
        sys::event::token(&self.inner)
    }

    /// Returns true if the event contains readable readiness.
    #[inline]
    pub fn is_readable(&self) -> bool {
        sys::event::is_readable(&self.inner)
    }

    /// Returns true if the event contains writable readiness.
    #[inline]
    pub fn is_writable(&self) -> bool {
        sys::event::is_writable(&self.inner)
    }

    /// Returns true if the event contains error readiness.
    ///
    /// Error events occur when the socket enters an error state. In this case,
    /// the socket will also receive a readable or writable event. Reading or
    /// writing to the socket will result in an error.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms (can) use
    /// this indicator.
    #[inline]
    pub fn is_error(&self) -> bool {
        sys::event::is_error(&self.inner)
    }

    /// Returns true if the event contains HUP readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms (can) use
    /// this indicator.
    ///
    /// When HUP events are received is different per platform. For example on
    /// epoll platforms HUP will be received when a TCP socket reached EOF,
    /// however on kqueue platforms a HUP event will be received when `shutdown`
    /// is called on the connection (both when shutting down the reading
    /// **and/or** writing side). Meaning that even though this function might
    /// return true on one platform in a given situation, it might return false
    /// on a different platform in the same situation. Furthermore even if true
    /// was returned on both platforms it could simply mean something different
    /// on the two platforms.
    ///
    /// Because of the above be cautions when using this in cross-platform
    /// applications, Mio makes no attempt at normalising this indicator and
    /// only provides a convenience method to read it.
    #[inline]
    pub fn is_hup(&self) -> bool {
        sys::event::is_hup(&self.inner)
    }

    /// Returns true if the event contains priority readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms (can) use
    /// this indicator.
    #[inline]
    pub fn is_priority(&self) -> bool {
        sys::event::is_priority(&self.inner)
    }

    /// Returns true if the event contains AIO readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms (can) use
    /// this indicator.
    #[inline]
    pub fn is_aio(&self) -> bool {
        sys::event::is_aio(&self.inner)
    }

    /// Returns true if the event contains LIO readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms (can) use
    /// this indicator.
    #[inline]
    pub fn is_lio(&self) -> bool {
        sys::event::is_lio(&self.inner)
    }

    /// Create a reference to an `Event` from a platform specific event.
    pub(crate) fn from_sys_event_ref(sys_event: &sys::Event) -> &Event {
        unsafe {
            // This is safe because only because the memory layout of `Event` is
            // the same as `sys::Event` due to the `repr(transparent)` attribute.
            std::mem::transmute(sys_event)
        }
    }
}

impl fmt::Debug for Event {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Event")
            .field("token", &self.token())
            .field("readable", &self.is_readable())
            .field("writable", &self.is_writable())
            .field("error", &self.is_error())
            .field("hup", &self.is_hup())
            .field("priority", &self.is_priority())
            .field("aio", &self.is_aio())
            .field("lio", &self.is_lio())
            .finish()
    }
}
<commit_msg>Prefer casting pointers over mem::transmute<commit_after>use crate::{sys, Token};

use std::fmt;

/// A readiness event.
///
/// `Event` is a readiness state paired with a [`Token`]. It is returned by
/// [`Poll::poll`].
///
/// For more documentation on polling and events, see [`Poll`].
///
/// [`Poll::poll`]: crate::Poll::poll
/// [`Poll`]: crate::Poll
/// [`Token`]: crate::Token
#[repr(transparent)]
pub struct Event {
    inner: sys::Event,
}

impl Event {
    /// Returns the event's token.
    #[inline]
    pub fn token(&self) -> Token {
        sys::event::token(&self.inner)
    }

    /// Returns true if the event contains readable readiness.
    #[inline]
    pub fn is_readable(&self) -> bool {
        sys::event::is_readable(&self.inner)
    }

    /// Returns true if the event contains writable readiness.
    #[inline]
    pub fn is_writable(&self) -> bool {
        sys::event::is_writable(&self.inner)
    }

    /// Returns true if the event contains error readiness.
    ///
    /// Error events occur when the socket enters an error state. In this case,
    /// the socket will also receive a readable or writable event. Reading or
    /// writing to the socket will result in an error.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms (can) use
    /// this indicator.
    #[inline]
    pub fn is_error(&self) -> bool {
        sys::event::is_error(&self.inner)
    }

    /// Returns true if the event contains HUP readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms (can) use
    /// this indicator.
    ///
    /// When HUP events are received is different per platform. For example on
    /// epoll platforms HUP will be received when a TCP socket reached EOF,
    /// however on kqueue platforms a HUP event will be received when `shutdown`
    /// is called on the connection (both when shutting down the reading
    /// **and/or** writing side). Meaning that even though this function might
    /// return true on one platform in a given situation, it might return false
    /// on a different platform in the same situation. Furthermore even if true
    /// was returned on both platforms it could simply mean something different
    /// on the two platforms.
    ///
    /// Because of the above be cautions when using this in cross-platform
    /// applications, Mio makes no attempt at normalising this indicator and
    /// only provides a convenience method to read it.
    #[inline]
    pub fn is_hup(&self) -> bool {
        sys::event::is_hup(&self.inner)
    }

    /// Returns true if the event contains priority readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms (can) use
    /// this indicator.
    #[inline]
    pub fn is_priority(&self) -> bool {
        sys::event::is_priority(&self.inner)
    }

    /// Returns true if the event contains AIO readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms (can) use
    /// this indicator.
    #[inline]
    pub fn is_aio(&self) -> bool {
        sys::event::is_aio(&self.inner)
    }

    /// Returns true if the event contains LIO readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms (can) use
    /// this indicator.
    #[inline]
    pub fn is_lio(&self) -> bool {
        sys::event::is_lio(&self.inner)
    }

    /// Create a reference to an `Event` from a platform specific event.
    pub(crate) fn from_sys_event_ref(sys_event: &sys::Event) -> &Event {
        unsafe {
            // This is safe because the memory layout of `Event` is
            // the same as `sys::Event` due to the `repr(transparent)` attribute.
            &*(sys_event as *const sys::Event as *const Event)
        }
    }
}

impl fmt::Debug for Event {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Event")
            .field("token", &self.token())
            .field("readable", &self.is_readable())
            .field("writable", &self.is_writable())
            .field("error", &self.is_error())
            .field("hup", &self.is_hup())
            .field("priority", &self.is_priority())
            .field("aio", &self.is_aio())
            .field("lio", &self.is_lio())
            .finish()
    }
}
<|endoftext|>


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

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

use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, ElementResult, SetAttributes};
use crate::error::{ElementError, ParseError, RenderingError};
use crate::length::*;
use crate::node::{Node, NodeBorrow};
use crate::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, FilterResult};

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

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

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

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

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

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

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

impl Default for Input {
    fn default() -> Self {
        Input::Unspecified
    }
}

impl Parse for Input {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        parser
            .try_parse(|p| {
                Ok(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 Primitive {
    /// Constructs a new `Primitive` with empty properties.
    #[inline]
    fn new() -> Primitive {
        Primitive {
            x: None,
            y: None,
            width: None,
            height: None,
            result: None,
        }
    }

    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
    #[inline]
    fn get_bounds(&self, ctx: &FilterContext) -> Result<BoundsBuilder, FilterError> {
        // With ObjectBoundingBox, only fractions and percents are allowed.
        if ctx.primitive_units() == CoordUnits::ObjectBoundingBox {
            check_units(self.x)?;
            check_units(self.y)?;
            check_units(self.width)?;
            check_units(self.height)?;
        }

        let transform = ctx.paffine();
        if !transform.is_invertible() {
            return Err(FilterError::InvalidParameter(
                "transform is not invertible".to_string(),
            ));
        }

        Ok(BoundsBuilder::new(
            self.x,
            self.y,
            self.width,
            self.height,
            transform,
        ))
    }
}

fn check_units<N: Normalize, V: Validate>(
    length: Option<CssLength<N, V>>,
) -> Result<(), FilterError> {
    match length {
        Some(l) if l.unit == LengthUnit::Px || l.unit == LengthUnit::Percent => Ok(()),
        Some(_) => Err(FilterError::InvalidUnits),
        None => Ok(()),
    }
}

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

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

        Ok((input_1, input_2))
    }

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

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

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

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

    if filter_node.borrow_element().is_in_error() {
        return Ok(source_surface);
    }

    let filter = borrow_element_as!(filter_node, Filter);

    let values = computed_from_node_being_filtered;

    let stroke_paint_source = values
        .stroke()
        .0
        .resolve(acquired_nodes, values.stroke_opacity().0, values.color().0)?
        .to_user_space(&node_bbox, draw_ctx, values);

    let fill_paint_source = values
        .fill()
        .0
        .resolve(acquired_nodes, values.fill_opacity().0, values.color().0)?
        .to_user_space(&node_bbox, draw_ctx, values);

    let mut filter_ctx = FilterContext::new(
        &filter,
        computed_from_node_being_filtered,
        stroke_paint_source,
        fill_paint_source,
        source_surface,
        draw_ctx,
        transform,
        node_bbox,
    );

    // If paffine is non-invertible, we won't draw anything. Also bbox combining in bounds
    // computations will panic due to non-invertible martrix.
    if !filter_ctx.paffine().is_invertible() {
        return Ok(filter_ctx.into_output()?);
    }

    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());

    for c in primitives {
        let elt = c.borrow_element();
        let filter = elt.as_filter_effect().unwrap();

        let start = Instant::now();

        if let Err(err) = filter
            .resolve(&c)
            .and_then(|params| render_primitive(&params, &filter_ctx, acquired_nodes, draw_ctx))
            .and_then(|result| filter_ctx.store_result(result))
        {
            rsvg_log!("(filter primitive {} returned an error: {})", c, err);

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

        let elapsed = start.elapsed();
        rsvg_log!(
            "(rendered filter primitive {} in\n    {} seconds)",
            c,
            elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
        );
    }

    Ok(filter_ctx.into_output()?)
}

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

    match params {
        Blend(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        ColorMatrix(p)       => p.render(ctx, acquired_nodes, draw_ctx),
        ComponentTransfer(p) => p.render(ctx, acquired_nodes, draw_ctx),
        Composite(p)         => p.render(ctx, acquired_nodes, draw_ctx),
        ConvolveMatrix(p)    => p.render(ctx, acquired_nodes, draw_ctx),
        DiffuseLighting(p)   => p.render(ctx, acquired_nodes, draw_ctx),
        DisplacementMap(p)   => p.render(ctx, acquired_nodes, draw_ctx),
        Flood(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        GaussianBlur(p)      => p.render(ctx, acquired_nodes, draw_ctx),
        Image(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        Merge(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        Morphology(p)        => p.render(ctx, acquired_nodes, draw_ctx),
        Offset(p)            => p.render(ctx, acquired_nodes, draw_ctx),
        SpecularLighting(p)  => p.render(ctx, acquired_nodes, draw_ctx),
        Tile(p)              => p.render(ctx, acquired_nodes, draw_ctx),
        Turbulence(p)        => p.render(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>Rename function<commit_after>//! Entry point for the CSS filters infrastructure.

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

use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, ElementResult, SetAttributes};
use crate::error::{ElementError, ParseError, RenderingError};
use crate::length::*;
use crate::node::{Node, NodeBorrow};
use crate::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, FilterResult};

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

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

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

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

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

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

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

impl Default for Input {
    fn default() -> Self {
        Input::Unspecified
    }
}

impl Parse for Input {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        parser
            .try_parse(|p| {
                Ok(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 Primitive {
    /// Constructs a new `Primitive` with empty properties.
    #[inline]
    fn new() -> Primitive {
        Primitive {
            x: None,
            y: None,
            width: None,
            height: None,
            result: None,
        }
    }

    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
    #[inline]
    fn get_bounds(&self, ctx: &FilterContext) -> Result<BoundsBuilder, FilterError> {
        // With ObjectBoundingBox, only fractions and percents are allowed.
        if ctx.primitive_units() == CoordUnits::ObjectBoundingBox {
            check_px_or_percent_units(self.x)?;
            check_px_or_percent_units(self.y)?;
            check_px_or_percent_units(self.width)?;
            check_px_or_percent_units(self.height)?;
        }

        let transform = ctx.paffine();
        if !transform.is_invertible() {
            return Err(FilterError::InvalidParameter(
                "transform is not invertible".to_string(),
            ));
        }

        Ok(BoundsBuilder::new(
            self.x,
            self.y,
            self.width,
            self.height,
            transform,
        ))
    }
}

fn check_px_or_percent_units<N: Normalize, V: Validate>(
    length: Option<CssLength<N, V>>,
) -> Result<(), FilterError> {
    match length {
        Some(l) if l.unit == LengthUnit::Px || l.unit == LengthUnit::Percent => Ok(()),
        Some(_) => Err(FilterError::InvalidUnits),
        None => Ok(()),
    }
}

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

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

        Ok((input_1, input_2))
    }

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

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

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

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

    if filter_node.borrow_element().is_in_error() {
        return Ok(source_surface);
    }

    let filter = borrow_element_as!(filter_node, Filter);

    let values = computed_from_node_being_filtered;

    let stroke_paint_source = values
        .stroke()
        .0
        .resolve(acquired_nodes, values.stroke_opacity().0, values.color().0)?
        .to_user_space(&node_bbox, draw_ctx, values);

    let fill_paint_source = values
        .fill()
        .0
        .resolve(acquired_nodes, values.fill_opacity().0, values.color().0)?
        .to_user_space(&node_bbox, draw_ctx, values);

    let mut filter_ctx = FilterContext::new(
        &filter,
        computed_from_node_being_filtered,
        stroke_paint_source,
        fill_paint_source,
        source_surface,
        draw_ctx,
        transform,
        node_bbox,
    );

    // If paffine is non-invertible, we won't draw anything. Also bbox combining in bounds
    // computations will panic due to non-invertible martrix.
    if !filter_ctx.paffine().is_invertible() {
        return Ok(filter_ctx.into_output()?);
    }

    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());

    for c in primitives {
        let elt = c.borrow_element();
        let filter = elt.as_filter_effect().unwrap();

        let start = Instant::now();

        if let Err(err) = filter
            .resolve(&c)
            .and_then(|params| render_primitive(&params, &filter_ctx, acquired_nodes, draw_ctx))
            .and_then(|result| filter_ctx.store_result(result))
        {
            rsvg_log!("(filter primitive {} returned an error: {})", c, err);

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

        let elapsed = start.elapsed();
        rsvg_log!(
            "(rendered filter primitive {} in\n    {} seconds)",
            c,
            elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
        );
    }

    Ok(filter_ctx.into_output()?)
}

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

    match params {
        Blend(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        ColorMatrix(p)       => p.render(ctx, acquired_nodes, draw_ctx),
        ComponentTransfer(p) => p.render(ctx, acquired_nodes, draw_ctx),
        Composite(p)         => p.render(ctx, acquired_nodes, draw_ctx),
        ConvolveMatrix(p)    => p.render(ctx, acquired_nodes, draw_ctx),
        DiffuseLighting(p)   => p.render(ctx, acquired_nodes, draw_ctx),
        DisplacementMap(p)   => p.render(ctx, acquired_nodes, draw_ctx),
        Flood(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        GaussianBlur(p)      => p.render(ctx, acquired_nodes, draw_ctx),
        Image(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        Merge(p)             => p.render(ctx, acquired_nodes, draw_ctx),
        Morphology(p)        => p.render(ctx, acquired_nodes, draw_ctx),
        Offset(p)            => p.render(ctx, acquired_nodes, draw_ctx),
        SpecularLighting(p)  => p.render(ctx, acquired_nodes, draw_ctx),
        Tile(p)              => p.render(ctx, acquired_nodes, draw_ctx),
        Turbulence(p)        => p.render(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 hyper::client::{RequestBuilder, Response};
use hyper::header::Headers;
use hyper::status::StatusCode;
use std::collections::HashMap;
use std::str;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use time;
use ::prelude_internal::*;

lazy_static! {
    static ref GLOBAL: Arc<Mutex<RateLimit>> = Arc::new(Mutex::new(RateLimit::default()));
    static ref ROUTES: Arc<Mutex<HashMap<Route, RateLimit>>> = Arc::new(Mutex::new(HashMap::default()));
}

/// Routes are used for ratelimiting. These are to differentiate between the
/// different _types_ of routes - such as getting the current user's channels -
/// for the most part, with the exception being major parameters.
///
/// [Taken from] the Discord docs, major parameters are:
///
/// > Additionally, rate limits take into account major parameters in the URL.
/// > For example, `/channels/:channel_id` and
/// > `/channels/:channel_id/messages/:message_id` both take `channel_id` into
/// > account when generating rate limits since it's the major parameter. The
/// only current major parameters are `channel_id` and `guild_id`.
///
/// This results in the two URIs of `GET /channels/4/messages/7` and
/// `GET /channels/5/messages/8` being rate limited _separately_. However, the
/// two URIs of `GET /channels/10/messages/11` and
/// `GET /channels/10/messages/12` will count towards the "same ratelimit", as
/// the major parameter - `10` is equivilant in both URIs.
///
/// # Examples
///
/// First: taking the first two URIs - `GET /channels/4/messages/7` and
/// `GET /channels/5/messages/8` - and assuming both buckets have a `limit` of
/// `10`, requesting the first URI will result in the response containing a
/// `remaining` of `9`. Immediately after - prior to buckets resetting -
/// performing a request to the _second_ URI will also contain a `remaining` of
/// `9` in the response, as the major parameter - `channel_id` - is different
/// in the two requests (`4` and `5`).
///
/// Second: take for example the last two URIs. Assuming the bucket's `limit` is
/// `10`, requesting the first URI will return a `remaining` of `9` in the
/// response. Immediately after - prior to buckets resetting - performing a
/// request to the _second_ URI will return a `remaining` of `8` in the
/// response, as the major parameter - `channel_id` - is equivilant for the two
/// requests (`10`).
///
///
/// With the examples out of the way: major parameters are why some variants
/// (i.e. all of the channel/guild variants) have an associated u64 as data.
/// This is the Id of the parameter, differentiating between different
/// ratelimits.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Route {
    ChannelsId(u64),
    ChannelsIdInvites(u64),
    ChannelsIdMessages(u64),
    ChannelsIdMessagesBulkDelete(u64),
    ChannelsIdMessagesId(u64),
    ChannelsIdMessagesIdAck(u64),
    ChannelsIdMessagesIdReactionsUserIdType(u64),
    ChannelsIdPermissionsOverwriteId(u64),
    ChannelsIdPins(u64),
    ChannelsIdPinsMessageId(u64),
    ChannelsIdTyping(u64),
    Gateway,
    GatewayBot,
    Global,
    Guilds,
    GuildsId(u64),
    GuildsIdBans(u64),
    GuildsIdBansUserId(u64),
    GuildsIdChannels(u64),
    GuildsIdEmbed(u64),
    GuildsIdEmojis(u64),
    GuildsIdEmojisId(u64),
    GuildsIdIntegrations(u64),
    GuildsIdIntegrationsId(u64),
    GuildsIdIntegrationsIdSync(u64),
    GuildsIdInvites(u64),
    GuildsIdMembers(u64),
    GuildsIdMembersId(u64),
    GuildsIdPrune(u64),
    GuildsIdRegions(u64),
    GuildsIdRoles(u64),
    GuildsIdRolesId(u64),
    InvitesCode,
    Users,
    UsersId,
    UsersMe,
    UsersMeChannels,
    USersMeConnections,
    UsersMeGuilds,
    UsersMeGuildsId,
    VoiceRegions,
    None,
}

pub fn perform<'a, F>(route: Route, f: F) -> Result<Response>
    where F: Fn() -> RequestBuilder<'a> {
    // Keeping the global lock poisoned here for the duration of the function
    // will ensure that requests are synchronous, which will further ensure
    // that 429s are _never_ hit.
    //
    // This would otherwise cause the potential for 429s to be hit while
    // requests are open.
    let mut global = GLOBAL.lock().expect("global route lock poisoned");

    loop {
        // Perform pre-checking here:
        //
        // - get the route's relevant rate
        // - sleep if that route's already rate-limited until the end of the
        //   'reset' time;
        // - get the global rate;
        // - sleep if there is 0 remaining
        // - then, perform the request
        global.pre_hook();

        if let Some(route) = ROUTES.lock().expect("routes poisoned").get_mut(&route) {
            route.pre_hook();
        }

        let response = try!(super::retry(&f));

        // Check if the request got ratelimited by checking for status 429,
        // and if so, sleep for the value of the header 'retry-after' -
        // which is in milliseconds - and then `continue` to try again
        //
        // If it didn't ratelimit, subtract one from the RateLimit's
        // 'remaining'
        //
        // Update the 'reset' with the value of the 'x-ratelimit-reset'
        // header
        //
        // It _may_ be possible for the limit to be raised at any time,
        // so check if it did from the value of the 'x-ratelimit-limit'
        // header. If the limit was 5 and is now 7, add 2 to the 'remaining'

        let redo = if response.headers.get_raw("x-ratelimit-global").is_some() {
            global.post_hook(&response)
        } else {
            ROUTES.lock()
                .expect("routes poisoned")
                .entry(route)
                .or_insert_with(RateLimit::default)
                .post_hook(&response)
        };

        if redo.unwrap_or(false) {
            continue;
        }

        return Ok(response);
    }
}

#[derive(Clone, Debug, Default)]
pub struct RateLimit {
    limit: i64,
    remaining: i64,
    reset: i64,
}

impl RateLimit {
    pub fn pre_hook(&mut self) {
        if self.limit == 0 {
            return;
        }

        let diff = (self.reset - time::get_time().sec) as u64;

        if self.remaining == 0 {
            let delay = (diff * 1000) + 500;

            debug!("Pre-emptive ratelimit for {:?}ms", delay);
            thread::sleep(Duration::from_millis(delay));

            return;
        }

        self.remaining -= 1;
    }

    pub fn post_hook(&mut self, response: &Response) -> Result<bool> {
        if let Some(limit) = try!(get_header(&response.headers, "x-ratelimit-limit")) {
            self.limit = limit;
        }

        if let Some(remaining) = try!(get_header(&response.headers, "x-ratelimit-remaining")) {
            self.remaining = remaining;
        }

        if let Some(reset) = try!(get_header(&response.headers, "x-ratelimit-reset")) {
            self.reset = reset;
        }

        Ok(if response.status != StatusCode::TooManyRequests {
            false
        } else if let Some(retry_after) = try!(get_header(&response.headers, "retry-after")) {
            debug!("Ratelimited: {:?}ms", retry_after);
            thread::sleep(Duration::from_millis(retry_after as u64));

            true
        } else {
            false
        })
    }
}

fn get_header(headers: &Headers, header: &str) -> Result<Option<i64>> {
    match headers.get_raw(header) {
        Some(header) => match str::from_utf8(&header[0]) {
            Ok(v) => match v.parse::<i64>() {
                Ok(v) => Ok(Some(v)),
                Err(_why) => Err(Error::Client(ClientError::RateLimitI64)),
            },
            Err(_why) => Err(Error::Client(ClientError::RateLimitUtf8)),
        },
        None => Ok(None),
    }
}
<commit_msg>Fix old ratelimit resets breaking next requests<commit_after>use hyper::client::{RequestBuilder, Response};
use hyper::header::Headers;
use hyper::status::StatusCode;
use std::collections::HashMap;
use std::str;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use time;
use ::prelude_internal::*;

lazy_static! {
    static ref GLOBAL: Arc<Mutex<RateLimit>> = Arc::new(Mutex::new(RateLimit::default()));
    static ref ROUTES: Arc<Mutex<HashMap<Route, RateLimit>>> = Arc::new(Mutex::new(HashMap::default()));
}

/// Routes are used for ratelimiting. These are to differentiate between the
/// different _types_ of routes - such as getting the current user's channels -
/// for the most part, with the exception being major parameters.
///
/// [Taken from] the Discord docs, major parameters are:
///
/// > Additionally, rate limits take into account major parameters in the URL.
/// > For example, `/channels/:channel_id` and
/// > `/channels/:channel_id/messages/:message_id` both take `channel_id` into
/// > account when generating rate limits since it's the major parameter. The
/// only current major parameters are `channel_id` and `guild_id`.
///
/// This results in the two URIs of `GET /channels/4/messages/7` and
/// `GET /channels/5/messages/8` being rate limited _separately_. However, the
/// two URIs of `GET /channels/10/messages/11` and
/// `GET /channels/10/messages/12` will count towards the "same ratelimit", as
/// the major parameter - `10` is equivilant in both URIs.
///
/// # Examples
///
/// First: taking the first two URIs - `GET /channels/4/messages/7` and
/// `GET /channels/5/messages/8` - and assuming both buckets have a `limit` of
/// `10`, requesting the first URI will result in the response containing a
/// `remaining` of `9`. Immediately after - prior to buckets resetting -
/// performing a request to the _second_ URI will also contain a `remaining` of
/// `9` in the response, as the major parameter - `channel_id` - is different
/// in the two requests (`4` and `5`).
///
/// Second: take for example the last two URIs. Assuming the bucket's `limit` is
/// `10`, requesting the first URI will return a `remaining` of `9` in the
/// response. Immediately after - prior to buckets resetting - performing a
/// request to the _second_ URI will return a `remaining` of `8` in the
/// response, as the major parameter - `channel_id` - is equivilant for the two
/// requests (`10`).
///
///
/// With the examples out of the way: major parameters are why some variants
/// (i.e. all of the channel/guild variants) have an associated u64 as data.
/// This is the Id of the parameter, differentiating between different
/// ratelimits.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Route {
    ChannelsId(u64),
    ChannelsIdInvites(u64),
    ChannelsIdMessages(u64),
    ChannelsIdMessagesBulkDelete(u64),
    ChannelsIdMessagesId(u64),
    ChannelsIdMessagesIdAck(u64),
    ChannelsIdMessagesIdReactionsUserIdType(u64),
    ChannelsIdPermissionsOverwriteId(u64),
    ChannelsIdPins(u64),
    ChannelsIdPinsMessageId(u64),
    ChannelsIdTyping(u64),
    Gateway,
    GatewayBot,
    Global,
    Guilds,
    GuildsId(u64),
    GuildsIdBans(u64),
    GuildsIdBansUserId(u64),
    GuildsIdChannels(u64),
    GuildsIdEmbed(u64),
    GuildsIdEmojis(u64),
    GuildsIdEmojisId(u64),
    GuildsIdIntegrations(u64),
    GuildsIdIntegrationsId(u64),
    GuildsIdIntegrationsIdSync(u64),
    GuildsIdInvites(u64),
    GuildsIdMembers(u64),
    GuildsIdMembersId(u64),
    GuildsIdPrune(u64),
    GuildsIdRegions(u64),
    GuildsIdRoles(u64),
    GuildsIdRolesId(u64),
    InvitesCode,
    Users,
    UsersId,
    UsersMe,
    UsersMeChannels,
    USersMeConnections,
    UsersMeGuilds,
    UsersMeGuildsId,
    VoiceRegions,
    None,
}

pub fn perform<'a, F>(route: Route, f: F) -> Result<Response>
    where F: Fn() -> RequestBuilder<'a> {
    // Keeping the global lock poisoned here for the duration of the function
    // will ensure that requests are synchronous, which will further ensure
    // that 429s are _never_ hit.
    //
    // This would otherwise cause the potential for 429s to be hit while
    // requests are open.
    let mut global = GLOBAL.lock().expect("global route lock poisoned");

    loop {
        // Perform pre-checking here:
        //
        // - get the route's relevant rate
        // - sleep if that route's already rate-limited until the end of the
        //   'reset' time;
        // - get the global rate;
        // - sleep if there is 0 remaining
        // - then, perform the request
        global.pre_hook();

        if let Some(route) = ROUTES.lock().expect("routes poisoned").get_mut(&route) {
            route.pre_hook();
        }

        let response = try!(super::retry(&f));

        // Check if the request got ratelimited by checking for status 429,
        // and if so, sleep for the value of the header 'retry-after' -
        // which is in milliseconds - and then `continue` to try again
        //
        // If it didn't ratelimit, subtract one from the RateLimit's
        // 'remaining'
        //
        // Update the 'reset' with the value of the 'x-ratelimit-reset'
        // header
        //
        // It _may_ be possible for the limit to be raised at any time,
        // so check if it did from the value of the 'x-ratelimit-limit'
        // header. If the limit was 5 and is now 7, add 2 to the 'remaining'

        let redo = if response.headers.get_raw("x-ratelimit-global").is_some() {
            global.post_hook(&response)
        } else {
            ROUTES.lock()
                .expect("routes poisoned")
                .entry(route)
                .or_insert_with(RateLimit::default)
                .post_hook(&response)
        };

        if redo.unwrap_or(false) {
            continue;
        }

        return Ok(response);
    }
}

#[derive(Clone, Debug, Default)]
pub struct RateLimit {
    limit: i64,
    remaining: i64,
    reset: i64,
}

impl RateLimit {
    pub fn pre_hook(&mut self) {
        if self.limit == 0 {
            return;
        }

        let current_time = time::get_time().sec;

        // The reset was in the past, so we're probably good.
        if current_time > self.reset {
            self.remaining = self.limit;

            return;
        }

        let diff = (self.reset - current_time) as u64;

        if self.remaining == 0 {
            let delay = (diff * 1000) + 500;

            debug!("Pre-emptive ratelimit for {:?}ms", delay);
            thread::sleep(Duration::from_millis(delay));

            return;
        }

        self.remaining -= 1;
    }

    pub fn post_hook(&mut self, response: &Response) -> Result<bool> {
        if let Some(limit) = try!(get_header(&response.headers, "x-ratelimit-limit")) {
            self.limit = limit;
        }

        if let Some(remaining) = try!(get_header(&response.headers, "x-ratelimit-remaining")) {
            self.remaining = remaining;
        }

        if let Some(reset) = try!(get_header(&response.headers, "x-ratelimit-reset")) {
            self.reset = reset;
        }

        Ok(if response.status != StatusCode::TooManyRequests {
            false
        } else if let Some(retry_after) = try!(get_header(&response.headers, "retry-after")) {
            debug!("Ratelimited: {:?}ms", retry_after);
            thread::sleep(Duration::from_millis(retry_after as u64));

            true
        } else {
            false
        })
    }
}

fn get_header(headers: &Headers, header: &str) -> Result<Option<i64>> {
    match headers.get_raw(header) {
        Some(header) => match str::from_utf8(&header[0]) {
            Ok(v) => match v.parse::<i64>() {
                Ok(v) => Ok(Some(v)),
                Err(_why) => Err(Error::Client(ClientError::RateLimitI64)),
            },
            Err(_why) => Err(Error::Client(ClientError::RateLimitUtf8)),
        },
        None => Ok(None),
    }
}
<|endoftext|>


<commit_before>use nom::{
  be_u8, be_u16,
  IResult,
  ErrorCode, Err,
};

use frame::{
  ChannelAssignment, NumberType,
  Frame,
  Header, Footer,
};

use metadata::StreamInfo;
use utility::{crc8, crc16, to_u32};

pub fn frame_parser<'a>(input: &'a [u8], stream_info: &StreamInfo)
                        -> IResult<'a, &'a [u8], Frame> {
  let result = chain!(input,
    frame_header: apply!(header, stream_info) ~
    frame_footer: footer,
    || {
      Frame {
        header: frame_header,
        footer: frame_footer,
      }
    }
  );

  match result {
    IResult::Done(i, frame)   => {
      // All frame bytes before the crc-16
      let end         = (input.len() - i.len()) - 2;
      let Footer(crc) = frame.footer;

      if crc16(&input[0..end]) == crc {
        IResult::Done(i, frame)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn blocking_strategy(input: &[u8]) -> IResult<&[u8], bool> {
  match take!(input, 2) {
    IResult::Done(i, bytes)   => {
      let sync_code = ((bytes[0] as u16) << 6) +
                      (bytes[1] as u16) >> 2;
      let is_valid  = ((bytes[1] >> 1) & 0b01) == 0;

      if sync_code == 0b11111111111110 && is_valid {
        let is_variable_block_size = (bytes[1] & 0b01) == 1;

        IResult::Done(i, is_variable_block_size)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn block_sample(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
  match be_u8(input) {
    IResult::Done(i, byte)    => {
      let block_byte  = byte >> 4;
      let sample_byte = byte & 0x0f;
      let is_valid    = block_byte != 0b0000 && sample_byte != 0b1111;

      if is_valid {
        IResult::Done(i, (block_byte, sample_byte))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn channel_bits(input: &[u8]) -> IResult<&[u8], (ChannelAssignment, u8)> {
  match be_u8(input) {
    IResult::Done(i, byte)    => {
      let channel_byte       = byte >> 4;
      let channel_assignment = match channel_byte {
        0b0000...0b0111 => ChannelAssignment::Independent,
        0b1000          => ChannelAssignment::LeftSide,
        0b1001          => ChannelAssignment::RightSide,
        0b1010          => ChannelAssignment::MiddleSide,
        _               => ChannelAssignment::Independent,
      };
      let size_byte = (byte >> 1) & 0b0111;

      // Checks the validity of:
      //
      // * (4 bits) channel assignment
      // * (3 bits) sample size
      // * (1 bit) last bit
      //
      // All these checks are whether of not they are reserved and should
      // return an error if so.
      let is_valid = channel_byte < 0b1011 &&
                     (size_byte != 0b0011 && size_byte != 0b0111) &&
                     (byte & 0b01) == 0;

      if is_valid {
        IResult::Done(i, (channel_assignment, size_byte))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn utf8_size(input: &[u8], is_u64: bool)
             -> IResult<&[u8], Option<(usize, u8)>> {
  map!(input, be_u8, |utf8_header| {
    match utf8_header {
      0b00000000...0b01111111 => Some((0, utf8_header)),
      0b11000000...0b11011111 => Some((1, utf8_header & 0b00011111)),
      0b11100000...0b11101111 => Some((2, utf8_header & 0b00001111)),
      0b11110000...0b11110111 => Some((3, utf8_header & 0b00000111)),
      0b11111000...0b11111011 => Some((4, utf8_header & 0b00000011)),
      0b11111100...0b11111101 => Some((5, utf8_header & 0b00000001)),
      0b11111110              => if is_u64 { Some((6, 0)) } else { None },
      _                       => None,
    }
  })
}

fn number_type(input: &[u8], is_sample: bool,
               (size, value): (usize, u8))
               -> IResult<&[u8], NumberType> {
  let mut result   = value as u64;
  let mut is_error = false;

  match take!(input, size) {
    IResult::Done(i, bytes)   => {
      for i in 0..size {
        let byte = bytes[i] as u64;

        if byte >= 0b10000000 && byte <= 0b10111111 {
          result = (result << 6) + (byte & 0b00111111);
        } else {
          is_error = true;
          break;
        }
      }

      if is_error {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      } else if is_sample {
        IResult::Done(i, NumberType::Sample(result))
      } else {
        IResult::Done(i, NumberType::Frame(result as u32))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn secondary_block_size(input: &[u8], block_byte: u8)
                        -> IResult<&[u8], Option<u32>> {
  match block_byte {
    0b0110 => opt!(input, map!(take!(1), to_u32)),
    0b0111 => opt!(input, map!(take!(2), to_u32)),
    _      => IResult::Done(input, None)
  }
}

fn secondary_sample_rate(input: &[u8], sample_byte: u8)
                        -> IResult<&[u8], Option<u32>> {
  match sample_byte {
    0b1100 => opt!(input, map!(take!(1), to_u32)),
    0b1101 => opt!(input, map!(take!(2), to_u32)),
    0b1110 => opt!(input, map!(take!(2), to_u32)),
    _      => IResult::Done(input, None)
  }
}

pub fn header<'a>(input: &'a [u8], stream_info: &StreamInfo)
                  -> IResult<'a, &'a [u8], Header> {
  let result = chain!(input,
    is_variable_block_size: blocking_strategy ~
    tuple0: block_sample ~
    tuple1: channel_bits ~
    number_opt: apply!(utf8_size, is_variable_block_size) ~
    number_length: expr_opt!(number_opt) ~
    number: apply!(number_type, is_variable_block_size, number_length) ~
    alt_block_size: apply!(secondary_block_size, tuple0.0) ~
    alt_sample_rate: apply!(secondary_sample_rate, tuple0.1) ~
    crc: be_u8,
    || {
      let (block_byte, sample_byte)       = tuple0;
      let (channel_assignment, size_byte) = tuple1;

      let block_size = match block_byte {
        0b0001          => 192,
        0b0010...0b0101 => 576 * 2_u32.pow(block_byte as u32 - 2),
        0b0110 | 0b0111 => alt_block_size.unwrap() + 1,
        0b1000...0b1111 => 256 * 2_u32.pow(block_byte as u32 - 8),
        _               => unreachable!(),
      };

      let sample_rate = match sample_byte {
        0b0000 => stream_info.sample_rate,
        0b0001 => 88200,
        0b0010 => 176400,
        0b0011 => 192000,
        0b0100 => 8000,
        0b0101 => 16000,
        0b0110 => 22050,
        0b0111 => 24000,
        0b1000 => 32000,
        0b1001 => 44100,
        0b1010 => 48000,
        0b1011 => 96000,
        0b1100 => alt_sample_rate.unwrap() * 1000,
        0b1101 => alt_sample_rate.unwrap(),
        0b1110 => alt_sample_rate.unwrap() * 10,
        _      => unreachable!(),
      };

      let bits_per_sample = match size_byte {
        0b0000 => stream_info.bits_per_sample as usize,
        0b0001 => 8,
        0b0010 => 12,
        0b0100 => 16,
        0b0101 => 20,
        0b0110 => 24,
        _      => unreachable!(),
      };

      Header {
        block_size: block_size,
        sample_rate: sample_rate,
        channel_assignment: channel_assignment,
        bits_per_sample: bits_per_sample,
        number: number,
        crc: crc,
      }
    }
  );

  match result {
    IResult::Done(i, frame_header) => {
      // All header bytes before the crc-8
      let end = (input.len() - i.len()) - 1;

      if crc8(&input[0..end]) == frame_header.crc {
        IResult::Done(i, frame_header)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)          => IResult::Error(error),
    IResult::Incomplete(need)      => IResult::Incomplete(need),
  }
}

named!(pub footer <&[u8], Footer>, map!(be_u16, Footer));

#[cfg(test)]
mod tests {
  use super::*;
  use frame:Footer;
  use nom::IResult;

  #[test]
  fn test_footer() {
    let input  = b"\x03\xe8";
    let result = IResult::Done(&[][..], Footer(0x03e8));

    assert_eq!(footer(input), result);
  }
}
<commit_msg>Add failing test for header<commit_after>use nom::{
  be_u8, be_u16,
  IResult,
  ErrorCode, Err,
};

use frame::{
  ChannelAssignment, NumberType,
  Frame,
  Header, Footer,
};

use metadata::StreamInfo;
use utility::{crc8, crc16, to_u32};

pub fn frame_parser<'a>(input: &'a [u8], stream_info: &StreamInfo)
                        -> IResult<'a, &'a [u8], Frame> {
  let result = chain!(input,
    frame_header: apply!(header, stream_info) ~
    frame_footer: footer,
    || {
      Frame {
        header: frame_header,
        footer: frame_footer,
      }
    }
  );

  match result {
    IResult::Done(i, frame)   => {
      // All frame bytes before the crc-16
      let end         = (input.len() - i.len()) - 2;
      let Footer(crc) = frame.footer;

      if crc16(&input[0..end]) == crc {
        IResult::Done(i, frame)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn blocking_strategy(input: &[u8]) -> IResult<&[u8], bool> {
  match take!(input, 2) {
    IResult::Done(i, bytes)   => {
      let sync_code = ((bytes[0] as u16) << 6) +
                      (bytes[1] as u16) >> 2;
      let is_valid  = ((bytes[1] >> 1) & 0b01) == 0;

      if sync_code == 0b11111111111110 && is_valid {
        let is_variable_block_size = (bytes[1] & 0b01) == 1;

        IResult::Done(i, is_variable_block_size)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn block_sample(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
  match be_u8(input) {
    IResult::Done(i, byte)    => {
      let block_byte  = byte >> 4;
      let sample_byte = byte & 0x0f;
      let is_valid    = block_byte != 0b0000 && sample_byte != 0b1111;

      if is_valid {
        IResult::Done(i, (block_byte, sample_byte))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn channel_bits(input: &[u8]) -> IResult<&[u8], (ChannelAssignment, u8)> {
  match be_u8(input) {
    IResult::Done(i, byte)    => {
      let channel_byte       = byte >> 4;
      let channel_assignment = match channel_byte {
        0b0000...0b0111 => ChannelAssignment::Independent,
        0b1000          => ChannelAssignment::LeftSide,
        0b1001          => ChannelAssignment::RightSide,
        0b1010          => ChannelAssignment::MiddleSide,
        _               => ChannelAssignment::Independent,
      };
      let size_byte = (byte >> 1) & 0b0111;

      // Checks the validity of:
      //
      // * (4 bits) channel assignment
      // * (3 bits) sample size
      // * (1 bit) last bit
      //
      // All these checks are whether of not they are reserved and should
      // return an error if so.
      let is_valid = channel_byte < 0b1011 &&
                     (size_byte != 0b0011 && size_byte != 0b0111) &&
                     (byte & 0b01) == 0;

      if is_valid {
        IResult::Done(i, (channel_assignment, size_byte))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn utf8_size(input: &[u8], is_u64: bool)
             -> IResult<&[u8], Option<(usize, u8)>> {
  map!(input, be_u8, |utf8_header| {
    match utf8_header {
      0b00000000...0b01111111 => Some((0, utf8_header)),
      0b11000000...0b11011111 => Some((1, utf8_header & 0b00011111)),
      0b11100000...0b11101111 => Some((2, utf8_header & 0b00001111)),
      0b11110000...0b11110111 => Some((3, utf8_header & 0b00000111)),
      0b11111000...0b11111011 => Some((4, utf8_header & 0b00000011)),
      0b11111100...0b11111101 => Some((5, utf8_header & 0b00000001)),
      0b11111110              => if is_u64 { Some((6, 0)) } else { None },
      _                       => None,
    }
  })
}

fn number_type(input: &[u8], is_sample: bool,
               (size, value): (usize, u8))
               -> IResult<&[u8], NumberType> {
  let mut result   = value as u64;
  let mut is_error = false;

  match take!(input, size) {
    IResult::Done(i, bytes)   => {
      for i in 0..size {
        let byte = bytes[i] as u64;

        if byte >= 0b10000000 && byte <= 0b10111111 {
          result = (result << 6) + (byte & 0b00111111);
        } else {
          is_error = true;
          break;
        }
      }

      if is_error {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      } else if is_sample {
        IResult::Done(i, NumberType::Sample(result))
      } else {
        IResult::Done(i, NumberType::Frame(result as u32))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn secondary_block_size(input: &[u8], block_byte: u8)
                        -> IResult<&[u8], Option<u32>> {
  match block_byte {
    0b0110 => opt!(input, map!(take!(1), to_u32)),
    0b0111 => opt!(input, map!(take!(2), to_u32)),
    _      => IResult::Done(input, None)
  }
}

fn secondary_sample_rate(input: &[u8], sample_byte: u8)
                        -> IResult<&[u8], Option<u32>> {
  match sample_byte {
    0b1100 => opt!(input, map!(take!(1), to_u32)),
    0b1101 => opt!(input, map!(take!(2), to_u32)),
    0b1110 => opt!(input, map!(take!(2), to_u32)),
    _      => IResult::Done(input, None)
  }
}

pub fn header<'a>(input: &'a [u8], stream_info: &StreamInfo)
                  -> IResult<'a, &'a [u8], Header> {
  let result = chain!(input,
    is_variable_block_size: blocking_strategy ~
    tuple0: block_sample ~
    tuple1: channel_bits ~
    number_opt: apply!(utf8_size, is_variable_block_size) ~
    number_length: expr_opt!(number_opt) ~
    number: apply!(number_type, is_variable_block_size, number_length) ~
    alt_block_size: apply!(secondary_block_size, tuple0.0) ~
    alt_sample_rate: apply!(secondary_sample_rate, tuple0.1) ~
    crc: be_u8,
    || {
      let (block_byte, sample_byte)       = tuple0;
      let (channel_assignment, size_byte) = tuple1;

      let block_size = match block_byte {
        0b0001          => 192,
        0b0010...0b0101 => 576 * 2_u32.pow(block_byte as u32 - 2),
        0b0110 | 0b0111 => alt_block_size.unwrap() + 1,
        0b1000...0b1111 => 256 * 2_u32.pow(block_byte as u32 - 8),
        _               => unreachable!(),
      };

      let sample_rate = match sample_byte {
        0b0000 => stream_info.sample_rate,
        0b0001 => 88200,
        0b0010 => 176400,
        0b0011 => 192000,
        0b0100 => 8000,
        0b0101 => 16000,
        0b0110 => 22050,
        0b0111 => 24000,
        0b1000 => 32000,
        0b1001 => 44100,
        0b1010 => 48000,
        0b1011 => 96000,
        0b1100 => alt_sample_rate.unwrap() * 1000,
        0b1101 => alt_sample_rate.unwrap(),
        0b1110 => alt_sample_rate.unwrap() * 10,
        _      => unreachable!(),
      };

      let bits_per_sample = match size_byte {
        0b0000 => stream_info.bits_per_sample as usize,
        0b0001 => 8,
        0b0010 => 12,
        0b0100 => 16,
        0b0101 => 20,
        0b0110 => 24,
        _      => unreachable!(),
      };

      Header {
        block_size: block_size,
        sample_rate: sample_rate,
        channel_assignment: channel_assignment,
        bits_per_sample: bits_per_sample,
        number: number,
        crc: crc,
      }
    }
  );

  match result {
    IResult::Done(i, frame_header) => {
      // All header bytes before the crc-8
      let end = (input.len() - i.len()) - 1;

      if crc8(&input[0..end]) == frame_header.crc {
        IResult::Done(i, frame_header)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)          => IResult::Error(error),
    IResult::Incomplete(need)      => IResult::Incomplete(need),
  }
}

named!(pub footer <&[u8], Footer>, map!(be_u16, Footer));

#[cfg(test)]
mod tests {
  use super::*;
  use frame::{
    Header, Footer,
    ChannelAssignment, NumberType,
  };
  use metadata::StreamInfo;
  use nom::IResult;

  #[test]
  fn test_header() {
    let input  = b"\xff\xf8\x53\x1c\xf0\x90\x80\x80\x2e";
    let info   = StreamInfo::new();
    let result = Header {
      block_size: 4608,
      sample_rate: 192000,
      channel_assignment: ChannelAssignment::Independent,
      bits_per_sample: 24,
      number: NumberType::Frame(65536),
      crc: 0x2e,
    };

    assert_eq!(header(input, &info), IResult::Done(&[][..], result));
  }

  #[test]
  fn test_footer() {
    let input  = b"\x03\xe8";
    let result = IResult::Done(&[][..], Footer(0x03e8));

    assert_eq!(footer(input), result);
  }
}
<|endoftext|>


<commit_before>use std::io;

use tokio_io::codec::{Encoder, Decoder};
use bytes::{BytesMut, BigEndian, ByteOrder};

use super::uint::{U31, U31Codec, U24, U24Codec};

macro_rules! try_some {
    ($expr: expr) => {
        match $expr {
            Some(s) => s,
            None => return Ok(None),
        }
    }
}

pub enum Frame {
    // +---------------+
    // |Pad Length? (8)|
    // +---------------+-----------------------------------------------+
    // |                            Data (*)                         ...
    // +---------------------------------------------------------------+
    // |                           Padding (*)                       ...
    // +---------------------------------------------------------------+
    Data {
        identifier: U31,
        end_stream: bool,
        pad_length: Option<u8>,
        data: Vec<u8>,
    },
    // +---------------+
    // |Pad Length? (8)|
    // +-+-------------+-----------------------------------------------+
    // |E|                 Stream Dependency? (31)                     |
    // +-+-------------+-----------------------------------------------+
    // |  Weight? (8)  |
    // +-+-------------+-----------------------------------------------+
    // |                   Header Block Fragment (*)                 ...
    // +---------------------------------------------------------------+
    // |                           Padding (*)                       ...
    // +---------------------------------------------------------------+
    Headers {
        identifier: U31,
        end_stream: bool,
        end_headers: bool,
        pad_length: Option<u8>,
        e: Option<bool>, // priority flag
        stream_dpendency: Option<U31>, // priority flag
        weight: Option<u8>, // priority flag
        header_block_fragment: Vec<u8>, // TODO
    },
    // +-+-------------------------------------------------------------+
    // |E|                  Stream Dependency (31)                     |
    // +-+-------------+-----------------------------------------------+
    // |   Weight (8)  |
    // +-+-------------+
    Priority {
        identifier: U31,
        e: bool,
        stream_dpendency: U31,
        weight: u8,
    },
    // +---------------------------------------------------------------+
    // |                        Error Code (32)                        |
    // +---------------------------------------------------------------+
    RstStream { identifier: U31, error_code: u32 },
    // +-------------------------------+
    // |       Identifier (16)         |
    // +-------------------------------+-------------------------------+
    // |                        Value (32)                             |
    // +---------------------------------------------------------------+
    // ...
    Settings {
        identifier: U31,
        ack: bool,
        settings: Vec<Setting>,
    },
    // +---------------+
    // |Pad Length? (8)|
    // +-+-------------+-----------------------------------------------+
    // |R|                  Promised Stream ID (31)                    |
    // +-+-----------------------------+-------------------------------+
    // |                   Header Block Fragment (*)                 ...
    // +---------------------------------------------------------------+
    // |                           Padding (*)                       ...
    // +---------------------------------------------------------------+
    PushPromise {
        identifier: U31,
        end_headers: bool,
        pad_length: Option<u8>,
        promised_stream_id: U31,
        header_block_fragment: Vec<u8>,
    },
    // +---------------------------------------------------------------+
    // |                                                               |
    // |                      Opaque Data (64)                         |
    // |                                                               |
    // +---------------------------------------------------------------+
    Ping {
        identifier: U31,
        ack: bool,
        opaque_data: u64,
    },
    // +-+-------------------------------------------------------------+
    // |R|                  Last-Stream-ID (31)                        |
    // +-+-------------------------------------------------------------+
    // |                      Error Code (32)                          |
    // +---------------------------------------------------------------+
    // |                  Additional Debug Data (*)                    |
    // +---------------------------------------------------------------+
    Goaway {
        identifier: U31,
        last_stream_id: U31,
        error_code: u32,
        additional_debug_data: Vec<u8>,
    },
    // +-+-------------------------------------------------------------+
    // |R|              Window Size Increment (31)                     |
    // +-+-------------------------------------------------------------+
    WindowUpdate {
        identifier: U31,
        window_size_increment: U31,
    },
    // +---------------------------------------------------------------+
    // |                   Header Block Fragment (*)                 ...
    // +---------------------------------------------------------------+
    Continuation {
        identifier: U31,
        end_headers: bool,
        header_block_fragment: Vec<u8>,
    },
}

pub struct FrameCodec {
    length: Option<U24>,
}

impl Default for FrameCodec {
    fn default() -> Self {
        FrameCodec { length: None }
    }
}

impl Decoder for FrameCodec {
    type Item = Frame;
    type Error = io::Error;

    // +-----------------------------------------------+
    // |                 Length (24)                   |
    // +---------------+---------------+---------------+
    // |   Type (8)    |   Flags (8)   |
    // +-+-------------+---------------+-------------------------------+
    // |R|                 Stream Identifier (31)                      |
    // +=+=============================================================+
    // |                   Frame Payload (0...)                      ...
    // +---------------------------------------------------------------+
    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        use self::Frame::*;
        if self.length.is_none() {
            self.length = Some(try_some!(U24Codec.decode(src)?));
        }
        let payload_length = self.length.unwrap().0 as usize;

        // frame header length = 9, `length` field length = 3, 9 - 3 = 6
        if src.len() < 6 + payload_length {
            return Ok(None);
        }

        let frame_type = src.split_to(1)[0];
        let flags = src.split_to(1)[0];
        let (_, identifier) = U31Codec.decode(&mut src.split_to(4))?.unwrap();
        let mut payload = src.split_to(payload_length);
        let frame = match frame_type {
            0x0 => {
                let end_stream = flags & 0x1 != 0;
                let pad_length = if flags & 0x8 != 0 {
                    let length = eat_padding(&mut payload)?;
                    Some(length)
                } else {
                    None
                };
                let data = Vec::from(&payload as &[u8]);
                Data {
                    identifier,
                    end_stream,
                    pad_length,
                    data,
                }
            }
            0x1 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                let end_stream = flags & 0x1 != 0;
                let end_headers = flags & 0x4 != 0;
                let pad_length = if flags & 0x8 != 0 {
                    let length = eat_padding(&mut payload)?;
                    Some(length)
                } else {
                    None
                };
                let (e, stream_dpendency, weight) = if flags & 0x20 != 0 {
                    if payload.len() < 5 {
                        // FRAME_SIZE_ERROR
                    }
                    let (e, stream_dpendency) = U31Codec.decode(&mut payload.split_to(4))?.unwrap();
                    let weight = payload.split_to(1)[0];
                    (Some(e), Some(stream_dpendency), Some(weight))
                } else {
                    (None, None, None)
                };
                let header_block_fragment = Vec::from(&payload as &[u8]);
                Headers {
                    identifier,
                    end_stream,
                    end_headers,
                    pad_length,
                    e,
                    stream_dpendency,
                    weight,
                    header_block_fragment,
                }
            }
            0x2 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                if payload.len() != 5 {
                    // FRAME_SIZE_ERROR
                }
                let (e, stream_dpendency) = U31Codec.decode(&mut payload.split_to(4))?.unwrap();
                let weight = payload[0];
                Priority {
                    identifier,
                    e,
                    stream_dpendency,
                    weight,
                }
            }
            0x3 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                if payload.len() != 4 {
                    // FRAME_SIZE_ERROR
                }
                let error_code = BigEndian::read_u32(&payload);
                RstStream {
                    identifier,
                    error_code,
                }
            }
            0x4 => {
                if identifier.0 != 0x0 {
                    // PROTOCOL_ERROR
                }
                if payload.len() % 6 != 0 {
                    // FRAME_SIZE_ERROR
                }
                let ack = flags & 0x1 != 0;
                if ack && payload.len() != 0 {
                    // FRAME_SIZE_ERROR
                }
                let n = payload.len() / 6;
                let mut settings = Vec::with_capacity(n);
                for _ in 0..n {
                    match SettingCodec.decode(src)?.unwrap() {
                        Setting::Unknown => (), // ignore
                        setting => settings.push(setting),
                    }
                }
                Settings {
                    identifier,
                    ack,
                    settings,
                }
            }
            0x5 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                let end_headers = flags & 0x4 != 0;
                let pad_length = if flags & 0x8 != 0 {
                    let length = eat_padding(&mut payload)?;
                    Some(length)
                } else {
                    None
                };
                if payload.len() < 4 {
                    // FRAME_SIZE_ERROR
                }
                let (_, promised_stream_id) = U31Codec.decode(&mut payload.split_to(4))?.unwrap();
                let header_block_fragment = Vec::from(&payload as &[u8]);
                PushPromise {
                    identifier,
                    end_headers,
                    pad_length,
                    promised_stream_id,
                    header_block_fragment,
                }
            }
            0x6 => {
                if identifier.0 != 0x0 {
                    // PROTOCOL_ERROR
                }
                if payload.len() != 8 {
                    // FRAME_SIZE_ERROR
                }
                let ack = flags & 0x1 != 0;
                let opaque_data = BigEndian::read_u64(&payload);
                Ping {
                    identifier,
                    ack,
                    opaque_data,
                }
            }
            0x7 => {
                if identifier.0 != 0x0 {
                    // PROTOCOL_ERROR
                }
                if payload.len() < 8 {
                    // FRAME_SIZE_ERROR
                }
                let (_, last_stream_id) = U31Codec.decode(&mut payload.split_to(4))?.unwrap();
                let error_code = BigEndian::read_u32(&payload.split_to(4));
                let additional_debug_data = Vec::from(&payload as &[u8]);
                // FIXME: 特定帧需要标识符必须为 0，检查
                Goaway {
                    identifier,
                    last_stream_id,
                    error_code,
                    additional_debug_data,
                }
            }
            0x8 => {
                if payload_length != 4 {
                    // FRAME_SIZE_ERROR
                }
                let (_, window_size_increment) = U31Codec.decode(&mut payload)?.unwrap();
                if window_size_increment.0 == 0 {
                    // PROTOCOL_ERROR
                }
                WindowUpdate {
                    identifier,
                    window_size_increment,
                }
            }
            0x9 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                let end_headers = flags & 0x4 != 0;
                let header_block_fragment = Vec::from(&payload as &[u8]);
                Continuation {
                    identifier,
                    end_headers,
                    header_block_fragment,
                }
            }
            _ => panic!(), // error
        };
        Ok(Some(frame))
    }
}

fn eat_padding(payload: &mut BytesMut) -> Result<u8, io::Error> {
    if payload.len() < 1 {
        // FRAME_SIZE_ERROR
    }
    let length = payload.split_to(1)[0];
    if payload.len() <= length as usize {
        // PROTOCOL_ERROR
    }
    let payload_len = payload.len(); // avoid mutable borrow checker
    payload.split_off(payload_len - length as usize);
    Ok(length)
}

impl Encoder for FrameCodec {
    type Item = Frame;
    type Error = io::Error;

    fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
        unimplemented!()
    }
}

// +-------------------------------+
// |       Identifier (16)         |
// +-------------------------------+-------------------------------+
// |                        Value (32)                             |
// +---------------------------------------------------------------+
pub enum Setting {
    HeaderTableSize(u32),
    EnablePush(bool),
    MaxConcurrentStreams(u32),
    InitialWindowSize(U31),
    MaxFrameSize(U24),
    MaxHeaderListSize(u32),
    Unknown,
}

struct SettingCodec;

impl Decoder for SettingCodec {
    type Item = Setting;
    type Error = io::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        use self::Setting::*;
        if src.len() < 6 {
            return Ok(None);
        }
        let setting_id = BigEndian::read_u16(&src.split_to(2));
        let setting_value = BigEndian::read_u32(&src.split_to(4));
        let r = match setting_id {
            0x1 => HeaderTableSize(setting_value),
            0x2 => EnablePush(setting_value == 0),
            0x3 => MaxConcurrentStreams(setting_value),
            0x4 => {
                if setting_value > U31::max_value().0 {
                    // PROTOCOL_ERROR
                }
                InitialWindowSize(U31(setting_value))
            }
            0x5 => {
                if setting_value < U24::initial_value().0 || setting_value > U24::max_value().0 {
                    // PROTOCOL_ERROR
                }
                MaxFrameSize(U24(setting_value))
            }
            0x6 => MaxHeaderListSize(setting_value),
            _ => Unknown,
        };
        Ok(Some(r))
    }
}
<commit_msg>Add missing error checker to DATA Frame<commit_after>use std::io;

use tokio_io::codec::{Encoder, Decoder};
use bytes::{BytesMut, BigEndian, ByteOrder};

use super::uint::{U31, U31Codec, U24, U24Codec};

macro_rules! try_some {
    ($expr: expr) => {
        match $expr {
            Some(s) => s,
            None => return Ok(None),
        }
    }
}

pub enum Frame {
    // +---------------+
    // |Pad Length? (8)|
    // +---------------+-----------------------------------------------+
    // |                            Data (*)                         ...
    // +---------------------------------------------------------------+
    // |                           Padding (*)                       ...
    // +---------------------------------------------------------------+
    Data {
        identifier: U31,
        end_stream: bool,
        pad_length: Option<u8>,
        data: Vec<u8>,
    },
    // +---------------+
    // |Pad Length? (8)|
    // +-+-------------+-----------------------------------------------+
    // |E|                 Stream Dependency? (31)                     |
    // +-+-------------+-----------------------------------------------+
    // |  Weight? (8)  |
    // +-+-------------+-----------------------------------------------+
    // |                   Header Block Fragment (*)                 ...
    // +---------------------------------------------------------------+
    // |                           Padding (*)                       ...
    // +---------------------------------------------------------------+
    Headers {
        identifier: U31,
        end_stream: bool,
        end_headers: bool,
        pad_length: Option<u8>,
        e: Option<bool>, // priority flag
        stream_dpendency: Option<U31>, // priority flag
        weight: Option<u8>, // priority flag
        header_block_fragment: Vec<u8>, // TODO
    },
    // +-+-------------------------------------------------------------+
    // |E|                  Stream Dependency (31)                     |
    // +-+-------------+-----------------------------------------------+
    // |   Weight (8)  |
    // +-+-------------+
    Priority {
        identifier: U31,
        e: bool,
        stream_dpendency: U31,
        weight: u8,
    },
    // +---------------------------------------------------------------+
    // |                        Error Code (32)                        |
    // +---------------------------------------------------------------+
    RstStream { identifier: U31, error_code: u32 },
    // +-------------------------------+
    // |       Identifier (16)         |
    // +-------------------------------+-------------------------------+
    // |                        Value (32)                             |
    // +---------------------------------------------------------------+
    // ...
    Settings {
        identifier: U31,
        ack: bool,
        settings: Vec<Setting>,
    },
    // +---------------+
    // |Pad Length? (8)|
    // +-+-------------+-----------------------------------------------+
    // |R|                  Promised Stream ID (31)                    |
    // +-+-----------------------------+-------------------------------+
    // |                   Header Block Fragment (*)                 ...
    // +---------------------------------------------------------------+
    // |                           Padding (*)                       ...
    // +---------------------------------------------------------------+
    PushPromise {
        identifier: U31,
        end_headers: bool,
        pad_length: Option<u8>,
        promised_stream_id: U31,
        header_block_fragment: Vec<u8>,
    },
    // +---------------------------------------------------------------+
    // |                                                               |
    // |                      Opaque Data (64)                         |
    // |                                                               |
    // +---------------------------------------------------------------+
    Ping {
        identifier: U31,
        ack: bool,
        opaque_data: u64,
    },
    // +-+-------------------------------------------------------------+
    // |R|                  Last-Stream-ID (31)                        |
    // +-+-------------------------------------------------------------+
    // |                      Error Code (32)                          |
    // +---------------------------------------------------------------+
    // |                  Additional Debug Data (*)                    |
    // +---------------------------------------------------------------+
    Goaway {
        identifier: U31,
        last_stream_id: U31,
        error_code: u32,
        additional_debug_data: Vec<u8>,
    },
    // +-+-------------------------------------------------------------+
    // |R|              Window Size Increment (31)                     |
    // +-+-------------------------------------------------------------+
    WindowUpdate {
        identifier: U31,
        window_size_increment: U31,
    },
    // +---------------------------------------------------------------+
    // |                   Header Block Fragment (*)                 ...
    // +---------------------------------------------------------------+
    Continuation {
        identifier: U31,
        end_headers: bool,
        header_block_fragment: Vec<u8>,
    },
}

pub struct FrameCodec {
    length: Option<U24>,
}

impl Default for FrameCodec {
    fn default() -> Self {
        FrameCodec { length: None }
    }
}

impl Decoder for FrameCodec {
    type Item = Frame;
    type Error = io::Error;

    // +-----------------------------------------------+
    // |                 Length (24)                   |
    // +---------------+---------------+---------------+
    // |   Type (8)    |   Flags (8)   |
    // +-+-------------+---------------+-------------------------------+
    // |R|                 Stream Identifier (31)                      |
    // +=+=============================================================+
    // |                   Frame Payload (0...)                      ...
    // +---------------------------------------------------------------+
    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        use self::Frame::*;
        if self.length.is_none() {
            self.length = Some(try_some!(U24Codec.decode(src)?));
        }
        let payload_length = self.length.unwrap().0 as usize;

        // frame header length = 9, `length` field length = 3, 9 - 3 = 6
        if src.len() < 6 + payload_length {
            return Ok(None);
        }

        let frame_type = src.split_to(1)[0];
        let flags = src.split_to(1)[0];
        let (_, identifier) = U31Codec.decode(&mut src.split_to(4))?.unwrap();
        let mut payload = src.split_to(payload_length);
        let frame = match frame_type {
            0x0 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                let end_stream = flags & 0x1 != 0;
                let pad_length = if flags & 0x8 != 0 {
                    let length = eat_padding(&mut payload)?;
                    Some(length)
                } else {
                    None
                };
                let data = Vec::from(&payload as &[u8]);
                Data {
                    identifier,
                    end_stream,
                    pad_length,
                    data,
                }
            }
            0x1 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                let end_stream = flags & 0x1 != 0;
                let end_headers = flags & 0x4 != 0;
                let pad_length = if flags & 0x8 != 0 {
                    let length = eat_padding(&mut payload)?;
                    Some(length)
                } else {
                    None
                };
                let (e, stream_dpendency, weight) = if flags & 0x20 != 0 {
                    if payload.len() < 5 {
                        // FRAME_SIZE_ERROR
                    }
                    let (e, stream_dpendency) = U31Codec.decode(&mut payload.split_to(4))?.unwrap();
                    let weight = payload.split_to(1)[0];
                    (Some(e), Some(stream_dpendency), Some(weight))
                } else {
                    (None, None, None)
                };
                let header_block_fragment = Vec::from(&payload as &[u8]);
                Headers {
                    identifier,
                    end_stream,
                    end_headers,
                    pad_length,
                    e,
                    stream_dpendency,
                    weight,
                    header_block_fragment,
                }
            }
            0x2 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                if payload.len() != 5 {
                    // FRAME_SIZE_ERROR
                }
                let (e, stream_dpendency) = U31Codec.decode(&mut payload.split_to(4))?.unwrap();
                let weight = payload[0];
                Priority {
                    identifier,
                    e,
                    stream_dpendency,
                    weight,
                }
            }
            0x3 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                if payload.len() != 4 {
                    // FRAME_SIZE_ERROR
                }
                let error_code = BigEndian::read_u32(&payload);
                RstStream {
                    identifier,
                    error_code,
                }
            }
            0x4 => {
                if identifier.0 != 0x0 {
                    // PROTOCOL_ERROR
                }
                if payload.len() % 6 != 0 {
                    // FRAME_SIZE_ERROR
                }
                let ack = flags & 0x1 != 0;
                if ack && payload.len() != 0 {
                    // FRAME_SIZE_ERROR
                }
                let n = payload.len() / 6;
                let mut settings = Vec::with_capacity(n);
                for _ in 0..n {
                    match SettingCodec.decode(src)?.unwrap() {
                        Setting::Unknown => (), // ignore
                        setting => settings.push(setting),
                    }
                }
                Settings {
                    identifier,
                    ack,
                    settings,
                }
            }
            0x5 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                let end_headers = flags & 0x4 != 0;
                let pad_length = if flags & 0x8 != 0 {
                    let length = eat_padding(&mut payload)?;
                    Some(length)
                } else {
                    None
                };
                if payload.len() < 4 {
                    // FRAME_SIZE_ERROR
                }
                let (_, promised_stream_id) = U31Codec.decode(&mut payload.split_to(4))?.unwrap();
                let header_block_fragment = Vec::from(&payload as &[u8]);
                PushPromise {
                    identifier,
                    end_headers,
                    pad_length,
                    promised_stream_id,
                    header_block_fragment,
                }
            }
            0x6 => {
                if identifier.0 != 0x0 {
                    // PROTOCOL_ERROR
                }
                if payload.len() != 8 {
                    // FRAME_SIZE_ERROR
                }
                let ack = flags & 0x1 != 0;
                let opaque_data = BigEndian::read_u64(&payload);
                Ping {
                    identifier,
                    ack,
                    opaque_data,
                }
            }
            0x7 => {
                if identifier.0 != 0x0 {
                    // PROTOCOL_ERROR
                }
                if payload.len() < 8 {
                    // FRAME_SIZE_ERROR
                }
                let (_, last_stream_id) = U31Codec.decode(&mut payload.split_to(4))?.unwrap();
                let error_code = BigEndian::read_u32(&payload.split_to(4));
                let additional_debug_data = Vec::from(&payload as &[u8]);
                // FIXME: 特定帧需要标识符必须为 0，检查
                Goaway {
                    identifier,
                    last_stream_id,
                    error_code,
                    additional_debug_data,
                }
            }
            0x8 => {
                if payload_length != 4 {
                    // FRAME_SIZE_ERROR
                }
                let (_, window_size_increment) = U31Codec.decode(&mut payload)?.unwrap();
                if window_size_increment.0 == 0 {
                    // PROTOCOL_ERROR
                }
                WindowUpdate {
                    identifier,
                    window_size_increment,
                }
            }
            0x9 => {
                if identifier.0 == 0x0 {
                    // PROTOCOL_ERROR
                }
                let end_headers = flags & 0x4 != 0;
                let header_block_fragment = Vec::from(&payload as &[u8]);
                Continuation {
                    identifier,
                    end_headers,
                    header_block_fragment,
                }
            }
            _ => panic!(), // error
        };
        Ok(Some(frame))
    }
}

fn eat_padding(payload: &mut BytesMut) -> Result<u8, io::Error> {
    if payload.len() < 1 {
        // FRAME_SIZE_ERROR
    }
    let length = payload.split_to(1)[0];
    if payload.len() <= length as usize {
        // PROTOCOL_ERROR
    }
    let payload_len = payload.len(); // avoid mutable borrow checker
    payload.split_off(payload_len - length as usize);
    Ok(length)
}

impl Encoder for FrameCodec {
    type Item = Frame;
    type Error = io::Error;

    fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
        unimplemented!()
    }
}

// +-------------------------------+
// |       Identifier (16)         |
// +-------------------------------+-------------------------------+
// |                        Value (32)                             |
// +---------------------------------------------------------------+
pub enum Setting {
    HeaderTableSize(u32),
    EnablePush(bool),
    MaxConcurrentStreams(u32),
    InitialWindowSize(U31),
    MaxFrameSize(U24),
    MaxHeaderListSize(u32),
    Unknown,
}

struct SettingCodec;

impl Decoder for SettingCodec {
    type Item = Setting;
    type Error = io::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        use self::Setting::*;
        if src.len() < 6 {
            return Ok(None);
        }
        let setting_id = BigEndian::read_u16(&src.split_to(2));
        let setting_value = BigEndian::read_u32(&src.split_to(4));
        let r = match setting_id {
            0x1 => HeaderTableSize(setting_value),
            0x2 => EnablePush(setting_value == 0),
            0x3 => MaxConcurrentStreams(setting_value),
            0x4 => {
                if setting_value > U31::max_value().0 {
                    // PROTOCOL_ERROR
                }
                InitialWindowSize(U31(setting_value))
            }
            0x5 => {
                if setting_value < U24::initial_value().0 || setting_value > U24::max_value().0 {
                    // PROTOCOL_ERROR
                }
                MaxFrameSize(U24(setting_value))
            }
            0x6 => MaxHeaderListSize(setting_value),
            _ => Unknown,
        };
        Ok(Some(r))
    }
}
<|endoftext|>