<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

// SVG 1.1 (Second Edition)
// https://www.w3.org/TR/SVG/
<% data.new_style_struct("InheritedSVG",
                         inherited=True,
                         gecko_name="SVG") %>

// Section 10 - Text

${helpers.single_keyword("text-anchor",
                         "start middle end",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty")}

// Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword("color-interpolation",
                         "srgb auto linearrgb",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty")}

${helpers.single_keyword("color-interpolation-filters", "linearrgb auto srgb",
                         products="gecko",
                         gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")}

${helpers.predefined_type(
    "fill", "SVGPaint",
    "::values::computed::SVGPaint::black()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")}

${helpers.predefined_type("fill-opacity", "Opacity", "1.0",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")}

${helpers.single_keyword("fill-rule", "nonzero evenodd",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty")}

${helpers.single_keyword("shape-rendering",
                         "auto optimizespeed crispedges geometricprecision",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")}

${helpers.predefined_type(
    "stroke", "SVGPaint",
    "Default::default()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}

${helpers.predefined_type(
    "stroke-width", "LengthOrPercentageOrNumber",
    "Either::First(1.0)",
    "parse_non_negative",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}

${helpers.single_keyword("stroke-linecap", "butt round square",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}

${helpers.single_keyword("stroke-linejoin", "miter round bevel",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")}

${helpers.predefined_type("stroke-miterlimit", "Number", "4.0",
                          "parse_at_least_one", products="gecko",
                          animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}

${helpers.predefined_type("stroke-opacity", "Opacity", "1.0",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}

${helpers.predefined_type("stroke-dasharray",
                          "LengthOrPercentageOrNumber",
                          "Either::First(0.0)",
                          "parse_non_negative",
                          vector="True",
                          delegate_animate="True",
                          allow_empty="True",
                          products="gecko",
                          animation_value_type="ComputedValue",
                          space_separated_allowed="True",
                          spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

${helpers.predefined_type(
    "stroke-dashoffset", "LengthOrPercentageOrNumber",
    "Either::First(0.0)",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

// Section 14 - Clipping, Masking and Compositing
${helpers.single_keyword("clip-rule", "nonzero evenodd",
                         products="gecko",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}

${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

<%helpers:longhand name="paint-order"
                   animation_value_type="none"
                   products="gecko"
                   spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">

    use values::computed::ComputedValueAsSpecified;
    use std::fmt;
    use style_traits::ToCss;

    pub const NORMAL: u8 = 0;
    pub const FILL: u8 = 1;
    pub const STROKE: u8 = 2;
    pub const MARKERS: u8 = 3;

    // number of bits for each component
    pub const SHIFT: u8 = 2;
    // mask with above bits set
    pub const MASK: u8 = 0b11;
    // number of non-normal keyword values
    pub const COUNT: u8 = 3;
    // all keywords
    pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS];

    /// Represented as a six-bit field, of 3 two-bit pairs
    ///
    /// Each pair can be set to FILL, STROKE, or MARKERS
    /// Lowest significant bit pairs are highest priority.
    ///  `normal` is the empty bitfield. The three pairs are
    /// never zero in any case other than `normal`.
    ///
    /// Higher priority values, i.e. the values specified first,
    /// will be painted first (and may be covered by paintings of lower priority)
    #[derive(PartialEq, Clone, Copy, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue(pub u8);

    pub mod computed_value {
        pub use super::SpecifiedValue as T;
    }

    pub fn get_initial_value() -> SpecifiedValue {
      SpecifiedValue(NORMAL)
    }

    impl SpecifiedValue {
        pub fn bits_at(&self, pos: u8) -> u8 {
            (self.0 >> pos * SHIFT) & MASK
        }
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue,ParseError<'i>> {
        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
            Ok(SpecifiedValue(0))
        } else {
            let mut value = 0;
            // bitfield representing what we've seen so far
            // bit 1 is fill, bit 2 is stroke, bit 3 is markers
            let mut seen = 0;
            let mut pos = 0;

            loop {
                let result: Result<_, ParseError> = input.try(|i| {
                    try_match_ident_ignore_ascii_case! { i.expect_ident()?,
                        "fill" => Ok(FILL),
                        "stroke" => Ok(STROKE),
                        "markers" => Ok(MARKERS),
                    }
                });

                match result {
                    Ok(val) => {
                        if (seen & (1 << val)) != 0 {
                            // don't parse the same ident twice
                            return Err(StyleParseError::UnspecifiedError.into())
                        } else {
                            value |= val << (pos * SHIFT);
                            seen |= 1 << val;
                            pos += 1;
                        }
                    }
                    Err(_) => break,
                }
            }

            if value == 0 {
                // couldn't find any keyword
                Err(StyleParseError::UnspecifiedError.into())
            } else {
                // fill in rest
                for i in pos..COUNT {
                    for paint in &ALL {
                        // if not seen, set bit at position, mark as seen
                        if (seen & (1 << paint)) == 0 {
                            seen |= 1 << paint;
                            value |= paint << (i * SHIFT);
                            break;
                        }
                    }
                }

                Ok(SpecifiedValue(value))
            }
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.0 == 0 {
                return dest.write_str("normal")
            }

            for pos in 0..COUNT {
                if pos != 0 {
                    dest.write_str(" ")?
                }
                match self.bits_at(pos) {
                    FILL => dest.write_str("fill")?,
                    STROKE => dest.write_str("stroke")?,
                    MARKERS => dest.write_str("markers")?,
                    _ => unreachable!(),
                }
            }
            Ok(())
        }
    }

    no_viewport_percentage!(SpecifiedValue);

    impl ComputedValueAsSpecified for SpecifiedValue { }
</%helpers:longhand>
<%helpers:vector_longhand name="-moz-context-properties"
                   animation_value_type="none"
                   products="gecko"
                   spec="Nonstandard (Internal-only)"
                   internal="True"
                   allow_empty="True">
    use values::CustomIdent;
    use values::computed::ComputedValueAsSpecified;

    no_viewport_percentage!(SpecifiedValue);

    impl ComputedValueAsSpecified for SpecifiedValue { }

    pub type SpecifiedValue = CustomIdent;

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue, ParseError<'i>> {
        let i = input.expect_ident()?;
        CustomIdent::from_ident(i, &["all", "none", "auto"])
    }
</%helpers:vector_longhand>
<commit_msg>Auto merge of #17424 - KuoE0:expose-moz-context-properties-on-stylo, r=heycam<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

// SVG 1.1 (Second Edition)
// https://www.w3.org/TR/SVG/
<% data.new_style_struct("InheritedSVG",
                         inherited=True,
                         gecko_name="SVG") %>

// Section 10 - Text

${helpers.single_keyword("text-anchor",
                         "start middle end",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty")}

// Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword("color-interpolation",
                         "srgb auto linearrgb",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty")}

${helpers.single_keyword("color-interpolation-filters", "linearrgb auto srgb",
                         products="gecko",
                         gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")}

${helpers.predefined_type(
    "fill", "SVGPaint",
    "::values::computed::SVGPaint::black()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")}

${helpers.predefined_type("fill-opacity", "Opacity", "1.0",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")}

${helpers.single_keyword("fill-rule", "nonzero evenodd",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty")}

${helpers.single_keyword("shape-rendering",
                         "auto optimizespeed crispedges geometricprecision",
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")}

${helpers.predefined_type(
    "stroke", "SVGPaint",
    "Default::default()",
    products="gecko",
    animation_value_type="IntermediateSVGPaint",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}

${helpers.predefined_type(
    "stroke-width", "LengthOrPercentageOrNumber",
    "Either::First(1.0)",
    "parse_non_negative",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}

${helpers.single_keyword("stroke-linecap", "butt round square",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}

${helpers.single_keyword("stroke-linejoin", "miter round bevel",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")}

${helpers.predefined_type("stroke-miterlimit", "Number", "4.0",
                          "parse_at_least_one", products="gecko",
                          animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}

${helpers.predefined_type("stroke-opacity", "Opacity", "1.0",
                          products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}

${helpers.predefined_type("stroke-dasharray",
                          "LengthOrPercentageOrNumber",
                          "Either::First(0.0)",
                          "parse_non_negative",
                          vector="True",
                          delegate_animate="True",
                          allow_empty="True",
                          products="gecko",
                          animation_value_type="ComputedValue",
                          space_separated_allowed="True",
                          spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

${helpers.predefined_type(
    "stroke-dashoffset", "LengthOrPercentageOrNumber",
    "Either::First(0.0)",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

// Section 14 - Clipping, Masking and Compositing
${helpers.single_keyword("clip-rule", "nonzero evenodd",
                         products="gecko",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}

${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          boxed="True" if product == "gecko" else "False",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

<%helpers:longhand name="paint-order"
                   animation_value_type="none"
                   products="gecko"
                   spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">

    use values::computed::ComputedValueAsSpecified;
    use std::fmt;
    use style_traits::ToCss;

    pub const NORMAL: u8 = 0;
    pub const FILL: u8 = 1;
    pub const STROKE: u8 = 2;
    pub const MARKERS: u8 = 3;

    // number of bits for each component
    pub const SHIFT: u8 = 2;
    // mask with above bits set
    pub const MASK: u8 = 0b11;
    // number of non-normal keyword values
    pub const COUNT: u8 = 3;
    // all keywords
    pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS];

    /// Represented as a six-bit field, of 3 two-bit pairs
    ///
    /// Each pair can be set to FILL, STROKE, or MARKERS
    /// Lowest significant bit pairs are highest priority.
    ///  `normal` is the empty bitfield. The three pairs are
    /// never zero in any case other than `normal`.
    ///
    /// Higher priority values, i.e. the values specified first,
    /// will be painted first (and may be covered by paintings of lower priority)
    #[derive(PartialEq, Clone, Copy, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue(pub u8);

    pub mod computed_value {
        pub use super::SpecifiedValue as T;
    }

    pub fn get_initial_value() -> SpecifiedValue {
      SpecifiedValue(NORMAL)
    }

    impl SpecifiedValue {
        pub fn bits_at(&self, pos: u8) -> u8 {
            (self.0 >> pos * SHIFT) & MASK
        }
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue,ParseError<'i>> {
        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
            Ok(SpecifiedValue(0))
        } else {
            let mut value = 0;
            // bitfield representing what we've seen so far
            // bit 1 is fill, bit 2 is stroke, bit 3 is markers
            let mut seen = 0;
            let mut pos = 0;

            loop {
                let result: Result<_, ParseError> = input.try(|i| {
                    try_match_ident_ignore_ascii_case! { i.expect_ident()?,
                        "fill" => Ok(FILL),
                        "stroke" => Ok(STROKE),
                        "markers" => Ok(MARKERS),
                    }
                });

                match result {
                    Ok(val) => {
                        if (seen & (1 << val)) != 0 {
                            // don't parse the same ident twice
                            return Err(StyleParseError::UnspecifiedError.into())
                        } else {
                            value |= val << (pos * SHIFT);
                            seen |= 1 << val;
                            pos += 1;
                        }
                    }
                    Err(_) => break,
                }
            }

            if value == 0 {
                // couldn't find any keyword
                Err(StyleParseError::UnspecifiedError.into())
            } else {
                // fill in rest
                for i in pos..COUNT {
                    for paint in &ALL {
                        // if not seen, set bit at position, mark as seen
                        if (seen & (1 << paint)) == 0 {
                            seen |= 1 << paint;
                            value |= paint << (i * SHIFT);
                            break;
                        }
                    }
                }

                Ok(SpecifiedValue(value))
            }
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.0 == 0 {
                return dest.write_str("normal")
            }

            for pos in 0..COUNT {
                if pos != 0 {
                    dest.write_str(" ")?
                }
                match self.bits_at(pos) {
                    FILL => dest.write_str("fill")?,
                    STROKE => dest.write_str("stroke")?,
                    MARKERS => dest.write_str("markers")?,
                    _ => unreachable!(),
                }
            }
            Ok(())
        }
    }

    no_viewport_percentage!(SpecifiedValue);

    impl ComputedValueAsSpecified for SpecifiedValue { }
</%helpers:longhand>
<%helpers:vector_longhand name="-moz-context-properties"
                   animation_value_type="none"
                   products="gecko"
                   spec="Nonstandard (Internal-only)"
                   allow_empty="True">
    use values::CustomIdent;
    use values::computed::ComputedValueAsSpecified;

    no_viewport_percentage!(SpecifiedValue);

    impl ComputedValueAsSpecified for SpecifiedValue { }

    pub type SpecifiedValue = CustomIdent;

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }

    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue, ParseError<'i>> {
        let i = input.expect_ident()?;
        CustomIdent::from_ident(i, &["all", "none", "auto"])
    }
</%helpers:vector_longhand>
<|endoftext|>


<commit_before>#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::iter::FromIterator;
use std::str::FromStr;
use syn::{Lit, LitFloat, LitInt};

fn lit(s: &str) -> Lit {
    match TokenStream::from_str(s)
        .unwrap()
        .into_iter()
        .next()
        .unwrap()
    {
        TokenTree::Literal(lit) => Lit::new(lit),
        _ => panic!(),
    }
}

#[test]
fn strings() {
    fn test_string(s: &str, value: &str) {
        match lit(s) {
            Lit::Str(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_string("\"a\"", "a");
    test_string("\"\\n\"", "\n");
    test_string("\"\\r\"", "\r");
    test_string("\"\\t\"", "\t");
    test_string("\"🐕\"", "🐕"); // NOTE: This is an emoji
    test_string("\"\\\"\"", "\"");
    test_string("\"'\"", "'");
    test_string("\"\"", "");
    test_string("\"\\u{1F415}\"", "\u{1F415}");
    test_string(
        "\"contains\nnewlines\\\nescaped newlines\"",
        "contains\nnewlinesescaped newlines",
    );
    test_string("r\"raw\nstring\\\nhere\"", "raw\nstring\\\nhere");
    test_string("\"...\"q", "...");
    test_string("r\"...\"q", "...");
    test_string("r##\"...\"##q", "...");
}

#[test]
fn byte_strings() {
    fn test_byte_string(s: &str, value: &[u8]) {
        match lit(s) {
            Lit::ByteStr(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_byte_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte_string("b\"a\"", b"a");
    test_byte_string("b\"\\n\"", b"\n");
    test_byte_string("b\"\\r\"", b"\r");
    test_byte_string("b\"\\t\"", b"\t");
    test_byte_string("b\"\\\"\"", b"\"");
    test_byte_string("b\"'\"", b"'");
    test_byte_string("b\"\"", b"");
    test_byte_string(
        "b\"contains\nnewlines\\\nescaped newlines\"",
        b"contains\nnewlinesescaped newlines",
    );
    test_byte_string("br\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere");
    test_byte_string("b\"...\"q", b"...");
    test_byte_string("br\"...\"q", b"...");
    test_byte_string("br##\"...\"##q", b"...");
}

#[test]
fn bytes() {
    fn test_byte(s: &str, value: u8) {
        match lit(s) {
            Lit::Byte(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                assert_eq!(again, s);
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte("b'a'", b'a');
    test_byte("b'\\n'", b'\n');
    test_byte("b'\\r'", b'\r');
    test_byte("b'\\t'", b'\t');
    test_byte("b'\\''", b'\'');
    test_byte("b'\"'", b'"');
    test_byte("b'a'q", b'a');
}

#[test]
fn chars() {
    fn test_char(s: &str, value: char) {
        match lit(s) {
            Lit::Char(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_char(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_char("'a'", 'a');
    test_char("'\\n'", '\n');
    test_char("'\\r'", '\r');
    test_char("'\\t'", '\t');
    test_char("'🐕'", '🐕'); // NOTE: This is an emoji
    test_char("'\\''", '\'');
    test_char("'\"'", '"');
    test_char("'\\u{1F415}'", '\u{1F415}');
    test_char("'a'q", 'a');
}

#[test]
fn ints() {
    fn test_int(s: &str, value: u64, suffix: &str) {
        match lit(s) {
            Lit::Int(lit) => {
                assert_eq!(lit.base10_digits().parse::<u64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_int(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_int("5", 5, "");
    test_int("5u32", 5, "u32");
    test_int("0o0A", 0, "A");
    test_int("5_0", 50, "");
    test_int("5_____0_____", 50, "");
    test_int("0x7f", 127, "");
    test_int("0x7F", 127, "");
    test_int("0b1001", 9, "");
    test_int("0o73", 59, "");
    test_int("0x7Fu8", 127, "u8");
    test_int("0b1001i8", 9, "i8");
    test_int("0o73u32", 59, "u32");
    test_int("0x__7___f_", 127, "");
    test_int("0x__7___F_", 127, "");
    test_int("0b_1_0__01", 9, "");
    test_int("0o_7__3", 59, "");
    test_int("0x_7F__u8", 127, "u8");
    test_int("0b__10__0_1i8", 9, "i8");
    test_int("0o__7__________________3u32", 59, "u32");
}

#[test]
fn floats() {
    #[cfg_attr(feature = "cargo-clippy", allow(float_cmp))]
    fn test_float(s: &str, value: f64, suffix: &str) {
        match lit(s) {
            Lit::Float(lit) => {
                assert_eq!(lit.base10_digits().parse::<f64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_float(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_float("5.5", 5.5, "");
    test_float("5.5E12", 5.5e12, "");
    test_float("5.5e12", 5.5e12, "");
    test_float("1.0__3e-12", 1.03e-12, "");
    test_float("1.03e+12", 1.03e12, "");
    test_float("9e99e99", 9e99, "e99");
}

#[test]
fn negative() {
    let span = Span::call_site();
    assert_eq!("-1", LitInt::new("-1", span).to_string());
    assert_eq!("-1i8", LitInt::new("-1i8", span).to_string());
    assert_eq!("-1i16", LitInt::new("-1i16", span).to_string());
    assert_eq!("-1i32", LitInt::new("-1i32", span).to_string());
    assert_eq!("-1i64", LitInt::new("-1i64", span).to_string());
    assert_eq!("-1.5", LitFloat::new("-1.5", span).to_string());
    assert_eq!("-1.5f32", LitFloat::new("-1.5f32", span).to_string());
    assert_eq!("-1.5f64", LitFloat::new("-1.5f64", span).to_string());
}

#[test]
fn negative_overflow() {
    assert!(syn::parse_str::<LitFloat>("-1.0e99f64").is_ok());
    assert!(syn::parse_str::<LitFloat>("-1.0e999f64").is_err());
}

#[test]
fn suffix() {
    fn get_suffix(token: &str) -> String {
        let lit = syn::parse_str::<Lit>(token).unwrap();
        match lit {
            Lit::Str(lit) => lit.suffix().to_owned(),
            Lit::ByteStr(lit) => lit.suffix().to_owned(),
            Lit::Byte(lit) => lit.suffix().to_owned(),
            Lit::Char(lit) => lit.suffix().to_owned(),
            Lit::Int(lit) => lit.suffix().to_owned(),
            Lit::Float(lit) => lit.suffix().to_owned(),
            _ => unimplemented!(),
        }
    }

    assert_eq!(get_suffix("\"\"s"), "s");
    assert_eq!(get_suffix("r\"\"r"), "r");
    assert_eq!(get_suffix("b\"\"b"), "b");
    assert_eq!(get_suffix("br\"\"br"), "br");
    assert_eq!(get_suffix("r#\"\"#r"), "r");
    assert_eq!(get_suffix("'c'c"), "c");
    assert_eq!(get_suffix("b'b'b"), "b");
    assert_eq!(get_suffix("1i32"), "i32");
    assert_eq!(get_suffix("1_i32"), "i32");
    assert_eq!(get_suffix("1.0f32"), "f32");
    assert_eq!(get_suffix("1.0_f32"), "f32");
}

#[test]
fn test_deep_group_empty() {
    let tokens = TokenStream::from_iter(vec![TokenTree::Group(Group::new(
        Delimiter::None,
        TokenStream::from_iter(vec![TokenTree::Group(Group::new(
            Delimiter::None,
            TokenStream::from_iter(vec![TokenTree::Literal(Literal::string("hi"))]),
        ))]),
    ))]);

    snapshot!(tokens as Lit, @r#""hi""# );
}
<commit_msg>Add test of integer literal with suffix starting with `E`<commit_after>#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::iter::FromIterator;
use std::str::FromStr;
use syn::{Lit, LitFloat, LitInt};

fn lit(s: &str) -> Lit {
    match TokenStream::from_str(s)
        .unwrap()
        .into_iter()
        .next()
        .unwrap()
    {
        TokenTree::Literal(lit) => Lit::new(lit),
        _ => panic!(),
    }
}

#[test]
fn strings() {
    fn test_string(s: &str, value: &str) {
        match lit(s) {
            Lit::Str(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_string("\"a\"", "a");
    test_string("\"\\n\"", "\n");
    test_string("\"\\r\"", "\r");
    test_string("\"\\t\"", "\t");
    test_string("\"🐕\"", "🐕"); // NOTE: This is an emoji
    test_string("\"\\\"\"", "\"");
    test_string("\"'\"", "'");
    test_string("\"\"", "");
    test_string("\"\\u{1F415}\"", "\u{1F415}");
    test_string(
        "\"contains\nnewlines\\\nescaped newlines\"",
        "contains\nnewlinesescaped newlines",
    );
    test_string("r\"raw\nstring\\\nhere\"", "raw\nstring\\\nhere");
    test_string("\"...\"q", "...");
    test_string("r\"...\"q", "...");
    test_string("r##\"...\"##q", "...");
}

#[test]
fn byte_strings() {
    fn test_byte_string(s: &str, value: &[u8]) {
        match lit(s) {
            Lit::ByteStr(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_byte_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte_string("b\"a\"", b"a");
    test_byte_string("b\"\\n\"", b"\n");
    test_byte_string("b\"\\r\"", b"\r");
    test_byte_string("b\"\\t\"", b"\t");
    test_byte_string("b\"\\\"\"", b"\"");
    test_byte_string("b\"'\"", b"'");
    test_byte_string("b\"\"", b"");
    test_byte_string(
        "b\"contains\nnewlines\\\nescaped newlines\"",
        b"contains\nnewlinesescaped newlines",
    );
    test_byte_string("br\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere");
    test_byte_string("b\"...\"q", b"...");
    test_byte_string("br\"...\"q", b"...");
    test_byte_string("br##\"...\"##q", b"...");
}

#[test]
fn bytes() {
    fn test_byte(s: &str, value: u8) {
        match lit(s) {
            Lit::Byte(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                assert_eq!(again, s);
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte("b'a'", b'a');
    test_byte("b'\\n'", b'\n');
    test_byte("b'\\r'", b'\r');
    test_byte("b'\\t'", b'\t');
    test_byte("b'\\''", b'\'');
    test_byte("b'\"'", b'"');
    test_byte("b'a'q", b'a');
}

#[test]
fn chars() {
    fn test_char(s: &str, value: char) {
        match lit(s) {
            Lit::Char(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_char(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_char("'a'", 'a');
    test_char("'\\n'", '\n');
    test_char("'\\r'", '\r');
    test_char("'\\t'", '\t');
    test_char("'🐕'", '🐕'); // NOTE: This is an emoji
    test_char("'\\''", '\'');
    test_char("'\"'", '"');
    test_char("'\\u{1F415}'", '\u{1F415}');
    test_char("'a'q", 'a');
}

#[test]
fn ints() {
    fn test_int(s: &str, value: u64, suffix: &str) {
        match lit(s) {
            Lit::Int(lit) => {
                assert_eq!(lit.base10_digits().parse::<u64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_int(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_int("5", 5, "");
    test_int("5u32", 5, "u32");
    test_int("0E", 0, "E");
    test_int("0ECMA", 0, "ECMA");
    test_int("0o0A", 0, "A");
    test_int("5_0", 50, "");
    test_int("5_____0_____", 50, "");
    test_int("0x7f", 127, "");
    test_int("0x7F", 127, "");
    test_int("0b1001", 9, "");
    test_int("0o73", 59, "");
    test_int("0x7Fu8", 127, "u8");
    test_int("0b1001i8", 9, "i8");
    test_int("0o73u32", 59, "u32");
    test_int("0x__7___f_", 127, "");
    test_int("0x__7___F_", 127, "");
    test_int("0b_1_0__01", 9, "");
    test_int("0o_7__3", 59, "");
    test_int("0x_7F__u8", 127, "u8");
    test_int("0b__10__0_1i8", 9, "i8");
    test_int("0o__7__________________3u32", 59, "u32");
}

#[test]
fn floats() {
    #[cfg_attr(feature = "cargo-clippy", allow(float_cmp))]
    fn test_float(s: &str, value: f64, suffix: &str) {
        match lit(s) {
            Lit::Float(lit) => {
                assert_eq!(lit.base10_digits().parse::<f64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_float(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_float("5.5", 5.5, "");
    test_float("5.5E12", 5.5e12, "");
    test_float("5.5e12", 5.5e12, "");
    test_float("1.0__3e-12", 1.03e-12, "");
    test_float("1.03e+12", 1.03e12, "");
    test_float("9e99e99", 9e99, "e99");
}

#[test]
fn negative() {
    let span = Span::call_site();
    assert_eq!("-1", LitInt::new("-1", span).to_string());
    assert_eq!("-1i8", LitInt::new("-1i8", span).to_string());
    assert_eq!("-1i16", LitInt::new("-1i16", span).to_string());
    assert_eq!("-1i32", LitInt::new("-1i32", span).to_string());
    assert_eq!("-1i64", LitInt::new("-1i64", span).to_string());
    assert_eq!("-1.5", LitFloat::new("-1.5", span).to_string());
    assert_eq!("-1.5f32", LitFloat::new("-1.5f32", span).to_string());
    assert_eq!("-1.5f64", LitFloat::new("-1.5f64", span).to_string());
}

#[test]
fn negative_overflow() {
    assert!(syn::parse_str::<LitFloat>("-1.0e99f64").is_ok());
    assert!(syn::parse_str::<LitFloat>("-1.0e999f64").is_err());
}

#[test]
fn suffix() {
    fn get_suffix(token: &str) -> String {
        let lit = syn::parse_str::<Lit>(token).unwrap();
        match lit {
            Lit::Str(lit) => lit.suffix().to_owned(),
            Lit::ByteStr(lit) => lit.suffix().to_owned(),
            Lit::Byte(lit) => lit.suffix().to_owned(),
            Lit::Char(lit) => lit.suffix().to_owned(),
            Lit::Int(lit) => lit.suffix().to_owned(),
            Lit::Float(lit) => lit.suffix().to_owned(),
            _ => unimplemented!(),
        }
    }

    assert_eq!(get_suffix("\"\"s"), "s");
    assert_eq!(get_suffix("r\"\"r"), "r");
    assert_eq!(get_suffix("b\"\"b"), "b");
    assert_eq!(get_suffix("br\"\"br"), "br");
    assert_eq!(get_suffix("r#\"\"#r"), "r");
    assert_eq!(get_suffix("'c'c"), "c");
    assert_eq!(get_suffix("b'b'b"), "b");
    assert_eq!(get_suffix("1i32"), "i32");
    assert_eq!(get_suffix("1_i32"), "i32");
    assert_eq!(get_suffix("1.0f32"), "f32");
    assert_eq!(get_suffix("1.0_f32"), "f32");
}

#[test]
fn test_deep_group_empty() {
    let tokens = TokenStream::from_iter(vec![TokenTree::Group(Group::new(
        Delimiter::None,
        TokenStream::from_iter(vec![TokenTree::Group(Group::new(
            Delimiter::None,
            TokenStream::from_iter(vec![TokenTree::Literal(Literal::string("hi"))]),
        ))]),
    ))]);

    snapshot!(tokens as Lit, @r#""hi""# );
}
<|endoftext|>


<commit_before>// xfail-fast
// aux-build:cci_class_cast.rs
use cci_class_cast;
import cci_class_cast::kitty::*;
import to_str::*;
import to_str::to_str;

fn print_out<T: to_str>(thing: T, expected: str) {
  let actual = thing.to_str();
  #debug("%s", actual);
  assert(actual == expected);
}

fn main() {
  let nyan : to_str  = cat(0u, 2, "nyan") as to_str;
  print_out(nyan, "nyan");
}

<commit_msg>test: Fix cyclic import in class-cast-to-iface-cross-crate-2<commit_after>// xfail-fast
// aux-build:cci_class_cast.rs
use cci_class_cast;
import to_str::*;
import cci_class_cast::kitty::*;

fn print_out<T: to_str>(thing: T, expected: str) {
  let actual = thing.to_str();
  #debug("%s", actual);
  assert(actual == expected);
}

fn main() {
  let nyan : to_str  = cat(0u, 2, "nyan") as to_str;
  print_out(nyan, "nyan");
}

<|endoftext|>


<commit_before>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Resource management and resolution for the virtio-video device.

use std::convert::TryInto;
use std::fmt;

use base::{
    self, FromRawDescriptor, IntoRawDescriptor, MemoryMappingArena, MemoryMappingBuilder,
    MemoryMappingBuilderUnix, MmapError, SafeDescriptor,
};
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};

use thiserror::Error as ThisError;

use crate::virtio::resource_bridge::{self, ResourceBridgeError, ResourceInfo, ResourceRequest};
use crate::virtio::video::format::{FramePlane, PlaneFormat};
use crate::virtio::video::protocol::{virtio_video_mem_entry, virtio_video_object_entry};

/// Defines how resources for a given queue are represented.
#[derive(Clone, Copy, Debug)]
pub enum ResourceType {
    /// Resources are backed by guest memory pages.
    GuestPages,
    /// Resources are backed by virtio objects.
    VirtioObject,
}

impl Default for ResourceType {
    fn default() -> Self {
        ResourceType::VirtioObject
    }
}

#[repr(C)]
#[derive(Clone, Copy)]
/// A guest resource entry which type is not decided yet.
pub union UnresolvedResourceEntry {
    pub object: virtio_video_object_entry,
    pub guest_mem: virtio_video_mem_entry,
}
unsafe impl data_model::DataInit for UnresolvedResourceEntry {}

impl fmt::Debug for UnresolvedResourceEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Safe because `self.object` and `self.guest_mem` are the same size and both made of
        // integers, making it safe to display them no matter their value.
        write!(
            f,
            "unresolved {:?} or {:?}",
            unsafe { self.object },
            unsafe { self.guest_mem }
        )
    }
}

/// Trait for types that can serve as video buffer backing memory.
pub trait BufferHandle: Sized {
    /// Try to clone this handle. This must only create a new reference to the same backing memory
    /// and not duplicate the buffer itself.
    fn try_clone(&self) -> Result<Self, base::Error>;

    /// Returns a linear mapping of [`offset`..`offset`+`size`] of the memory backing this buffer.
    fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError>;
}

/// Linear memory area of a `GuestMemHandle`
#[derive(Clone)]
pub struct GuestMemArea {
    /// Offset within the guest region to the start of the area.
    pub offset: u64,
    /// Length of the area within the memory region.
    pub length: usize,
}

pub struct GuestMemHandle {
    /// Descriptor to the guest memory region containing the buffer.
    pub desc: SafeDescriptor,
    /// Memory areas (i.e. sg list) that make the memory buffer.
    pub mem_areas: Vec<GuestMemArea>,
}

impl BufferHandle for GuestMemHandle {
    fn try_clone(&self) -> Result<Self, base::Error> {
        Ok(Self {
            desc: self.desc.try_clone()?,
            mem_areas: self.mem_areas.clone(),
        })
    }

    fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
        let mut arena = MemoryMappingArena::new(size)?;
        let mut mapped_size = 0;
        let mut area_iter = self.mem_areas.iter();
        let mut area_offset = offset;
        while mapped_size < size {
            let area = match area_iter.next() {
                Some(area) => area,
                None => {
                    return Err(MmapError::InvalidRange(
                        offset,
                        size,
                        self.mem_areas.iter().map(|a| a.length).sum(),
                    ));
                }
            };
            if area_offset > area.length {
                area_offset -= area.length;
            } else {
                let mapping_length = std::cmp::min(area.length - area_offset, size - mapped_size);
                arena.add_fd_offset(mapped_size, mapping_length, &self.desc, area.offset)?;
                mapped_size += mapping_length;
                area_offset = 0;
            }
        }
        Ok(arena)
    }
}

pub struct VirtioObjectHandle {
    /// Descriptor for the object.
    pub desc: SafeDescriptor,
    /// Modifier to apply to frame resources.
    pub modifier: u64,
}

impl BufferHandle for VirtioObjectHandle {
    fn try_clone(&self) -> Result<Self, base::Error> {
        Ok(Self {
            desc: self.desc.try_clone()?,
            modifier: self.modifier,
        })
    }

    fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
        MemoryMappingBuilder::new(size)
            .from_descriptor(&self.desc)
            .offset(offset as u64)
            .build()
            .map(MemoryMappingArena::from)
    }
}

pub enum GuestResourceHandle {
    GuestPages(GuestMemHandle),
    VirtioObject(VirtioObjectHandle),
}

impl BufferHandle for GuestResourceHandle {
    fn try_clone(&self) -> Result<Self, base::Error> {
        Ok(match self {
            Self::GuestPages(handle) => Self::GuestPages(handle.try_clone()?),
            Self::VirtioObject(handle) => Self::VirtioObject(handle.try_clone()?),
        })
    }

    fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
        match self {
            GuestResourceHandle::GuestPages(handle) => handle.get_mapping(offset, size),
            GuestResourceHandle::VirtioObject(handle) => handle.get_mapping(offset, size),
        }
    }
}

pub struct GuestResource {
    /// Handle to the backing memory.
    pub handle: GuestResourceHandle,
    /// Layout of color planes, if the resource will receive frames.
    pub planes: Vec<FramePlane>,
}

#[derive(Debug, ThisError)]
pub enum GuestMemResourceCreationError {
    #[error("Provided slice of entries is empty")]
    NoEntriesProvided,
    #[error("cannot get shm region: {0}")]
    CantGetShmRegion(GuestMemoryError),
    #[error("cannot get shm offset: {0}")]
    CantGetShmOffset(GuestMemoryError),
    #[error("error while cloning shm region descriptor: {0}")]
    DescriptorCloneError(base::Error),
}

#[derive(Debug, ThisError)]
pub enum ObjectResourceCreationError {
    #[error("uuid {0:08} is larger than 32 bits")]
    UuidNot32Bits(u128),
    #[error("resource returned by bridge is not a buffer")]
    NotABuffer,
    #[error("resource bridge failure: {0}")]
    ResourceBridgeFailure(ResourceBridgeError),
}

impl GuestResource {
    /// Try to convert an unresolved virtio guest memory entry into a resolved guest memory
    /// resource.
    ///
    /// Convert `mem_entry` into the guest memory resource it represents and resolve it through
    /// `mem`. `planes_format` describes the format of the individual planes for the buffer.
    pub fn from_virtio_guest_mem_entry(
        mem_entries: &[virtio_video_mem_entry],
        mem: &GuestMemory,
        planes_format: &[PlaneFormat],
    ) -> Result<GuestResource, GuestMemResourceCreationError> {
        let region_desc = match mem_entries.first() {
            None => return Err(GuestMemResourceCreationError::NoEntriesProvided),
            Some(entry) => {
                let addr: u64 = entry.addr.into();

                let guest_region = mem
                    .shm_region(GuestAddress(addr))
                    .map_err(GuestMemResourceCreationError::CantGetShmRegion)?;
                let desc = base::clone_descriptor(guest_region)
                    .map_err(GuestMemResourceCreationError::DescriptorCloneError)?;
                // Safe because we are the sole owner of the duplicated descriptor.
                unsafe { SafeDescriptor::from_raw_descriptor(desc) }
            }
        };

        let mem_areas = mem_entries
            .into_iter()
            .map(|entry| {
                let addr: u64 = entry.addr.into();
                let length: u32 = entry.length.into();
                let region_offset = mem
                    .offset_from_base(GuestAddress(addr))
                    .map_err(GuestMemResourceCreationError::CantGetShmOffset)
                    .unwrap();

                GuestMemArea {
                    offset: region_offset,
                    length: length as usize,
                }
            })
            .collect();

        // The plane information can be computed from the currently set format.
        let mut buffer_offset = 0;
        let planes = planes_format
            .iter()
            .map(|p| {
                let plane_offset = buffer_offset;
                buffer_offset += p.plane_size;

                FramePlane {
                    offset: plane_offset as usize,
                    stride: p.stride as usize,
                }
            })
            .collect();

        Ok(GuestResource {
            handle: GuestResourceHandle::GuestPages(GuestMemHandle {
                desc: region_desc,
                mem_areas,
            }),
            planes,
        })
    }

    /// Try to convert an unresolved virtio object entry into a resolved object resource.
    ///
    /// Convert `object` into the object resource it represents and resolve it through `res_bridge`.
    /// Returns an error if the object's UUID is invalid or cannot be resolved to a buffer object
    /// by `res_bridge`.
    pub fn from_virtio_object_entry(
        object: virtio_video_object_entry,
        res_bridge: &base::Tube,
    ) -> Result<GuestResource, ObjectResourceCreationError> {
        // We trust that the caller has chosen the correct object type.
        let uuid = u128::from_be_bytes(object.uuid);

        // TODO(stevensd): `Virtio3DBackend::resource_assign_uuid` is currently implemented to use
        // 32-bits resource_handles as UUIDs. Once it starts using real UUIDs, we need to update
        // this conversion.
        let handle = TryInto::<u32>::try_into(uuid)
            .map_err(|_| ObjectResourceCreationError::UuidNot32Bits(uuid))?;

        let buffer_info = match resource_bridge::get_resource_info(
            res_bridge,
            ResourceRequest::GetBuffer { id: handle },
        ) {
            Ok(ResourceInfo::Buffer(buffer_info)) => buffer_info,
            Ok(_) => return Err(ObjectResourceCreationError::NotABuffer),
            Err(e) => return Err(ObjectResourceCreationError::ResourceBridgeFailure(e)),
        };

        Ok(GuestResource {
            handle: GuestResourceHandle::VirtioObject(VirtioObjectHandle {
                // Safe because `buffer_info.file` is a valid file descriptor and we are stealing
                // it.
                desc: unsafe {
                    SafeDescriptor::from_raw_descriptor(buffer_info.file.into_raw_descriptor())
                },
                modifier: buffer_info.modifier,
            }),
            planes: buffer_info
                .planes
                .iter()
                .take_while(|p| p.offset != 0 || p.stride != 0)
                .map(|p| FramePlane {
                    offset: p.offset as usize,
                    stride: p.stride as usize,
                })
                .collect(),
        })
    }

    #[cfg(feature = "video-encoder")]
    pub fn try_clone(&self) -> Result<Self, base::Error> {
        Ok(Self {
            handle: self.handle.try_clone()?,
            planes: self.planes.clone(),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use base::{MappedRegion, SafeDescriptor, SharedMemory};

    /// Creates a sparse guest memory handle using as many pages as there are entries in
    /// `page_order`. The page with index `0` will be the first page, `1` will be the second page,
    /// etc.
    ///
    /// The memory handle is filled with increasing u32s starting from page 0, then page 1, and so
    /// on. Finally the handle is mapped into a linear space and we check that the written integers
    /// appear in the expected order.
    fn check_guest_mem_handle(page_order: &[usize]) {
        const PAGE_SIZE: usize = 0x1000;
        const U32_SIZE: usize = std::mem::size_of::<u32>();
        const ENTRIES_PER_PAGE: usize = PAGE_SIZE as usize / std::mem::size_of::<u32>();

        // Fill a vector of the same size as the handle with u32s of increasing value, following
        // the page layout given as argument.
        let mut data = vec![0u8; PAGE_SIZE * page_order.len()];
        for (page_index, page) in page_order.iter().enumerate() {
            let page_slice =
                &mut data[(page * PAGE_SIZE as usize)..((page + 1) * PAGE_SIZE as usize)];
            for (index, chunk) in page_slice.chunks_exact_mut(4).enumerate() {
                let sized_chunk: &mut [u8; 4] = chunk.try_into().unwrap();
                *sized_chunk = (((page_index * ENTRIES_PER_PAGE) + index) as u32).to_ne_bytes();
            }
        }

        // Copy the initialized vector's content into an anonymous shared memory.
        let mem = SharedMemory::anon(data.len() as u64).unwrap();
        let mapping = MemoryMappingBuilder::new(mem.size() as usize)
            .from_shared_memory(&mem)
            .build()
            .unwrap();
        assert_eq!(mapping.write_slice(&data, 0).unwrap(), data.len());

        // Create the `GuestMemHandle` we will try to map and retrieve the data from.
        let mem_handle = GuestResourceHandle::GuestPages(GuestMemHandle {
            desc: unsafe {
                SafeDescriptor::from_raw_descriptor(base::clone_descriptor(&mem).unwrap())
            },
            mem_areas: page_order
                .iter()
                .map(|&page| GuestMemArea {
                    offset: page as u64 * PAGE_SIZE as u64,
                    length: PAGE_SIZE as usize,
                })
                .collect(),
        });

        // Map the handle into a linear memory area, retrieve its data into a new vector, and check
        // that its u32s appear to increase linearly.
        let mapping = mem_handle.get_mapping(0, mem.size() as usize).unwrap();
        let mut data = vec![0u8; PAGE_SIZE * page_order.len()];
        unsafe { std::ptr::copy_nonoverlapping(mapping.as_ptr(), data.as_mut_ptr(), data.len()) };
        for (index, chunk) in data.chunks_exact(U32_SIZE).enumerate() {
            let sized_chunk: &[u8; 4] = chunk.try_into().unwrap();
            assert_eq!(u32::from_ne_bytes(*sized_chunk), index as u32);
        }
    }

    // Fill a guest memory handle with a single memory page.
    // Then check that the data can be properly mapped and appears in the expected order.
    #[test]
    fn test_single_guest_mem_handle() {
        check_guest_mem_handle(&[0])
    }

    // Fill a guest memory handle with 4 memory pages that are contiguous.
    // Then check that the pages appear in the expected order in the mapping.
    #[test]
    fn test_linear_guest_mem_handle() {
        check_guest_mem_handle(&[0, 1, 2, 3])
    }

    // Fill a guest memory handle with 8 pages mapped in non-linear order.
    // Then check that the pages appear in the expected order in the mapping.
    #[test]
    fn test_sparse_guest_mem_handle() {
        check_guest_mem_handle(&[1, 7, 6, 3, 5, 0, 4, 2])
    }
}
<commit_msg>virtio: video: fix clippy warning<commit_after>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Resource management and resolution for the virtio-video device.

use std::convert::TryInto;
use std::fmt;

use base::{
    self, FromRawDescriptor, IntoRawDescriptor, MemoryMappingArena, MemoryMappingBuilder,
    MemoryMappingBuilderUnix, MmapError, SafeDescriptor,
};
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};

use thiserror::Error as ThisError;

use crate::virtio::resource_bridge::{self, ResourceBridgeError, ResourceInfo, ResourceRequest};
use crate::virtio::video::format::{FramePlane, PlaneFormat};
use crate::virtio::video::protocol::{virtio_video_mem_entry, virtio_video_object_entry};

/// Defines how resources for a given queue are represented.
#[derive(Clone, Copy, Debug)]
pub enum ResourceType {
    /// Resources are backed by guest memory pages.
    GuestPages,
    /// Resources are backed by virtio objects.
    VirtioObject,
}

impl Default for ResourceType {
    fn default() -> Self {
        ResourceType::VirtioObject
    }
}

#[repr(C)]
#[derive(Clone, Copy)]
/// A guest resource entry which type is not decided yet.
pub union UnresolvedResourceEntry {
    pub object: virtio_video_object_entry,
    pub guest_mem: virtio_video_mem_entry,
}
unsafe impl data_model::DataInit for UnresolvedResourceEntry {}

impl fmt::Debug for UnresolvedResourceEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Safe because `self.object` and `self.guest_mem` are the same size and both made of
        // integers, making it safe to display them no matter their value.
        write!(
            f,
            "unresolved {:?} or {:?}",
            unsafe { self.object },
            unsafe { self.guest_mem }
        )
    }
}

/// Trait for types that can serve as video buffer backing memory.
pub trait BufferHandle: Sized {
    /// Try to clone this handle. This must only create a new reference to the same backing memory
    /// and not duplicate the buffer itself.
    fn try_clone(&self) -> Result<Self, base::Error>;

    /// Returns a linear mapping of [`offset`..`offset`+`size`] of the memory backing this buffer.
    fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError>;
}

/// Linear memory area of a `GuestMemHandle`
#[derive(Clone)]
pub struct GuestMemArea {
    /// Offset within the guest region to the start of the area.
    pub offset: u64,
    /// Length of the area within the memory region.
    pub length: usize,
}

pub struct GuestMemHandle {
    /// Descriptor to the guest memory region containing the buffer.
    pub desc: SafeDescriptor,
    /// Memory areas (i.e. sg list) that make the memory buffer.
    pub mem_areas: Vec<GuestMemArea>,
}

impl BufferHandle for GuestMemHandle {
    fn try_clone(&self) -> Result<Self, base::Error> {
        Ok(Self {
            desc: self.desc.try_clone()?,
            mem_areas: self.mem_areas.clone(),
        })
    }

    fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
        let mut arena = MemoryMappingArena::new(size)?;
        let mut mapped_size = 0;
        let mut area_iter = self.mem_areas.iter();
        let mut area_offset = offset;
        while mapped_size < size {
            let area = match area_iter.next() {
                Some(area) => area,
                None => {
                    return Err(MmapError::InvalidRange(
                        offset,
                        size,
                        self.mem_areas.iter().map(|a| a.length).sum(),
                    ));
                }
            };
            if area_offset > area.length {
                area_offset -= area.length;
            } else {
                let mapping_length = std::cmp::min(area.length - area_offset, size - mapped_size);
                arena.add_fd_offset(mapped_size, mapping_length, &self.desc, area.offset)?;
                mapped_size += mapping_length;
                area_offset = 0;
            }
        }
        Ok(arena)
    }
}

pub struct VirtioObjectHandle {
    /// Descriptor for the object.
    pub desc: SafeDescriptor,
    /// Modifier to apply to frame resources.
    pub modifier: u64,
}

impl BufferHandle for VirtioObjectHandle {
    fn try_clone(&self) -> Result<Self, base::Error> {
        Ok(Self {
            desc: self.desc.try_clone()?,
            modifier: self.modifier,
        })
    }

    fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
        MemoryMappingBuilder::new(size)
            .from_descriptor(&self.desc)
            .offset(offset as u64)
            .build()
            .map(MemoryMappingArena::from)
    }
}

pub enum GuestResourceHandle {
    GuestPages(GuestMemHandle),
    VirtioObject(VirtioObjectHandle),
}

impl BufferHandle for GuestResourceHandle {
    fn try_clone(&self) -> Result<Self, base::Error> {
        Ok(match self {
            Self::GuestPages(handle) => Self::GuestPages(handle.try_clone()?),
            Self::VirtioObject(handle) => Self::VirtioObject(handle.try_clone()?),
        })
    }

    fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
        match self {
            GuestResourceHandle::GuestPages(handle) => handle.get_mapping(offset, size),
            GuestResourceHandle::VirtioObject(handle) => handle.get_mapping(offset, size),
        }
    }
}

pub struct GuestResource {
    /// Handle to the backing memory.
    pub handle: GuestResourceHandle,
    /// Layout of color planes, if the resource will receive frames.
    pub planes: Vec<FramePlane>,
}

#[derive(Debug, ThisError)]
pub enum GuestMemResourceCreationError {
    #[error("Provided slice of entries is empty")]
    NoEntriesProvided,
    #[error("cannot get shm region: {0}")]
    CantGetShmRegion(GuestMemoryError),
    #[error("cannot get shm offset: {0}")]
    CantGetShmOffset(GuestMemoryError),
    #[error("error while cloning shm region descriptor: {0}")]
    DescriptorCloneError(base::Error),
}

#[derive(Debug, ThisError)]
pub enum ObjectResourceCreationError {
    #[error("uuid {0:08} is larger than 32 bits")]
    UuidNot32Bits(u128),
    #[error("resource returned by bridge is not a buffer")]
    NotABuffer,
    #[error("resource bridge failure: {0}")]
    ResourceBridgeFailure(ResourceBridgeError),
}

impl GuestResource {
    /// Try to convert an unresolved virtio guest memory entry into a resolved guest memory
    /// resource.
    ///
    /// Convert `mem_entry` into the guest memory resource it represents and resolve it through
    /// `mem`. `planes_format` describes the format of the individual planes for the buffer.
    pub fn from_virtio_guest_mem_entry(
        mem_entries: &[virtio_video_mem_entry],
        mem: &GuestMemory,
        planes_format: &[PlaneFormat],
    ) -> Result<GuestResource, GuestMemResourceCreationError> {
        let region_desc = match mem_entries.first() {
            None => return Err(GuestMemResourceCreationError::NoEntriesProvided),
            Some(entry) => {
                let addr: u64 = entry.addr.into();

                let guest_region = mem
                    .shm_region(GuestAddress(addr))
                    .map_err(GuestMemResourceCreationError::CantGetShmRegion)?;
                let desc = base::clone_descriptor(guest_region)
                    .map_err(GuestMemResourceCreationError::DescriptorCloneError)?;
                // Safe because we are the sole owner of the duplicated descriptor.
                unsafe { SafeDescriptor::from_raw_descriptor(desc) }
            }
        };

        let mem_areas = mem_entries
            .iter()
            .map(|entry| {
                let addr: u64 = entry.addr.into();
                let length: u32 = entry.length.into();
                let region_offset = mem
                    .offset_from_base(GuestAddress(addr))
                    .map_err(GuestMemResourceCreationError::CantGetShmOffset)
                    .unwrap();

                GuestMemArea {
                    offset: region_offset,
                    length: length as usize,
                }
            })
            .collect();

        // The plane information can be computed from the currently set format.
        let mut buffer_offset = 0;
        let planes = planes_format
            .iter()
            .map(|p| {
                let plane_offset = buffer_offset;
                buffer_offset += p.plane_size;

                FramePlane {
                    offset: plane_offset as usize,
                    stride: p.stride as usize,
                }
            })
            .collect();

        Ok(GuestResource {
            handle: GuestResourceHandle::GuestPages(GuestMemHandle {
                desc: region_desc,
                mem_areas,
            }),
            planes,
        })
    }

    /// Try to convert an unresolved virtio object entry into a resolved object resource.
    ///
    /// Convert `object` into the object resource it represents and resolve it through `res_bridge`.
    /// Returns an error if the object's UUID is invalid or cannot be resolved to a buffer object
    /// by `res_bridge`.
    pub fn from_virtio_object_entry(
        object: virtio_video_object_entry,
        res_bridge: &base::Tube,
    ) -> Result<GuestResource, ObjectResourceCreationError> {
        // We trust that the caller has chosen the correct object type.
        let uuid = u128::from_be_bytes(object.uuid);

        // TODO(stevensd): `Virtio3DBackend::resource_assign_uuid` is currently implemented to use
        // 32-bits resource_handles as UUIDs. Once it starts using real UUIDs, we need to update
        // this conversion.
        let handle = TryInto::<u32>::try_into(uuid)
            .map_err(|_| ObjectResourceCreationError::UuidNot32Bits(uuid))?;

        let buffer_info = match resource_bridge::get_resource_info(
            res_bridge,
            ResourceRequest::GetBuffer { id: handle },
        ) {
            Ok(ResourceInfo::Buffer(buffer_info)) => buffer_info,
            Ok(_) => return Err(ObjectResourceCreationError::NotABuffer),
            Err(e) => return Err(ObjectResourceCreationError::ResourceBridgeFailure(e)),
        };

        Ok(GuestResource {
            handle: GuestResourceHandle::VirtioObject(VirtioObjectHandle {
                // Safe because `buffer_info.file` is a valid file descriptor and we are stealing
                // it.
                desc: unsafe {
                    SafeDescriptor::from_raw_descriptor(buffer_info.file.into_raw_descriptor())
                },
                modifier: buffer_info.modifier,
            }),
            planes: buffer_info
                .planes
                .iter()
                .take_while(|p| p.offset != 0 || p.stride != 0)
                .map(|p| FramePlane {
                    offset: p.offset as usize,
                    stride: p.stride as usize,
                })
                .collect(),
        })
    }

    #[cfg(feature = "video-encoder")]
    pub fn try_clone(&self) -> Result<Self, base::Error> {
        Ok(Self {
            handle: self.handle.try_clone()?,
            planes: self.planes.clone(),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use base::{MappedRegion, SafeDescriptor, SharedMemory};

    /// Creates a sparse guest memory handle using as many pages as there are entries in
    /// `page_order`. The page with index `0` will be the first page, `1` will be the second page,
    /// etc.
    ///
    /// The memory handle is filled with increasing u32s starting from page 0, then page 1, and so
    /// on. Finally the handle is mapped into a linear space and we check that the written integers
    /// appear in the expected order.
    fn check_guest_mem_handle(page_order: &[usize]) {
        const PAGE_SIZE: usize = 0x1000;
        const U32_SIZE: usize = std::mem::size_of::<u32>();
        const ENTRIES_PER_PAGE: usize = PAGE_SIZE as usize / std::mem::size_of::<u32>();

        // Fill a vector of the same size as the handle with u32s of increasing value, following
        // the page layout given as argument.
        let mut data = vec![0u8; PAGE_SIZE * page_order.len()];
        for (page_index, page) in page_order.iter().enumerate() {
            let page_slice =
                &mut data[(page * PAGE_SIZE as usize)..((page + 1) * PAGE_SIZE as usize)];
            for (index, chunk) in page_slice.chunks_exact_mut(4).enumerate() {
                let sized_chunk: &mut [u8; 4] = chunk.try_into().unwrap();
                *sized_chunk = (((page_index * ENTRIES_PER_PAGE) + index) as u32).to_ne_bytes();
            }
        }

        // Copy the initialized vector's content into an anonymous shared memory.
        let mem = SharedMemory::anon(data.len() as u64).unwrap();
        let mapping = MemoryMappingBuilder::new(mem.size() as usize)
            .from_shared_memory(&mem)
            .build()
            .unwrap();
        assert_eq!(mapping.write_slice(&data, 0).unwrap(), data.len());

        // Create the `GuestMemHandle` we will try to map and retrieve the data from.
        let mem_handle = GuestResourceHandle::GuestPages(GuestMemHandle {
            desc: unsafe {
                SafeDescriptor::from_raw_descriptor(base::clone_descriptor(&mem).unwrap())
            },
            mem_areas: page_order
                .iter()
                .map(|&page| GuestMemArea {
                    offset: page as u64 * PAGE_SIZE as u64,
                    length: PAGE_SIZE as usize,
                })
                .collect(),
        });

        // Map the handle into a linear memory area, retrieve its data into a new vector, and check
        // that its u32s appear to increase linearly.
        let mapping = mem_handle.get_mapping(0, mem.size() as usize).unwrap();
        let mut data = vec![0u8; PAGE_SIZE * page_order.len()];
        unsafe { std::ptr::copy_nonoverlapping(mapping.as_ptr(), data.as_mut_ptr(), data.len()) };
        for (index, chunk) in data.chunks_exact(U32_SIZE).enumerate() {
            let sized_chunk: &[u8; 4] = chunk.try_into().unwrap();
            assert_eq!(u32::from_ne_bytes(*sized_chunk), index as u32);
        }
    }

    // Fill a guest memory handle with a single memory page.
    // Then check that the data can be properly mapped and appears in the expected order.
    #[test]
    fn test_single_guest_mem_handle() {
        check_guest_mem_handle(&[0])
    }

    // Fill a guest memory handle with 4 memory pages that are contiguous.
    // Then check that the pages appear in the expected order in the mapping.
    #[test]
    fn test_linear_guest_mem_handle() {
        check_guest_mem_handle(&[0, 1, 2, 3])
    }

    // Fill a guest memory handle with 8 pages mapped in non-linear order.
    // Then check that the pages appear in the expected order in the mapping.
    #[test]
    fn test_sparse_guest_mem_handle() {
        check_guest_mem_handle(&[1, 7, 6, 3, 5, 0, 4, 2])
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(transmute_from_fn_item_types)]

use std::mem;

unsafe fn foo() -> (isize, *const (), Option<fn()>) {
    let i = mem::transmute(bar);
    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
    //~^^ ERROR was previously accepted

    let p = mem::transmute(foo);
    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
    //~^^ ERROR was previously accepted

    let of = mem::transmute(main);
    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
    //~^^ ERROR was previously accepted

    (i, p, of)
}

unsafe fn bar() {
    mem::transmute::<_, *mut ()>(foo);
    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
    //~^^ ERROR was previously accepted

    mem::transmute::<_, fn()>(bar);
    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
    //~^^ ERROR was previously accepted

    // No error if a coercion would otherwise occur.
    mem::transmute::<fn(), usize>(main);
}

fn main() {
    unsafe {
        foo();
        bar();
    }
}
<commit_msg>fix `transmute-from-fn-item-types-lint`<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(transmute_from_fn_item_types)]

use std::mem;

unsafe fn foo() -> (isize, *const (), Option<fn()>) {
    let i = mem::transmute(bar);
    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
    //~^^ WARNING was previously accepted

    let p = mem::transmute(foo);
    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
    //~^^ WARNING was previously accepted

    let of = mem::transmute(main);
    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
    //~^^ WARNING was previously accepted

    (i, p, of)
}

unsafe fn bar() {
    mem::transmute::<_, *mut ()>(foo);
    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
    //~^^ WARNING was previously accepted

    mem::transmute::<_, fn()>(bar);
    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
    //~^^ WARNING was previously accepted

    // No error if a coercion would otherwise occur.
    mem::transmute::<fn(), usize>(main);
}

fn main() {
    unsafe {
        foo();
        bar();
    }
}
<|endoftext|>