Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/servo/components/style/values/specified/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 16 kB image not shown  

Quelle  number.rs

  Sprache: Rust
 

/* 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 https://mozilla.org/MPL/2.0/. */


//! Specified numbers and integers.

use crate::derives::*;
use crate::parser::{Parse, ParserContext};
use crate::typed_om::{ToTyped, TypedValue};
use crate::values::computed::transform::DirectionVector;
use crate::values::computed::{Context, ToComputedValue};
use crate::values::generics::transform::IsParallelTo;
use crate::values::generics::{GreaterThanOrEqualToOne, NonNegative};
use crate::values::specified::calc::{CalcNode, CalcNumeric, Leaf};
use crate::values::specified::{NoCalcPercentage, Percentage};
use crate::values::tagged_numeric::{NumericUnion, Unpacked, UnpackedMut};
use crate::values::{serialize_number, CSSFloat, CSSInteger};
use crate::{One, Zero};
use cssparser::{Parser, Token};
use std::fmt::{self, Write};
use style_traits::values::specified::AllowedNumericType;
use style_traits::{CssWriter, ParseError, ParsingMode, SpecifiedValueInfo, ToCss};
use thin_vec::ThinVec;

/// Parse a `<number>` value, with a given clamping mode.
pub fn parse_number_with_clamping_mode<'i, 't>(
    context: &ParserContext,
    input: &mut Parser<'i, 't>,
    clamping_mode: AllowedNumericType,
) -> Result<Number, ParseError<'i>> {
    let location = input.current_source_location();
    Ok(Number(match *input.next()? {
        Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => {
            NumericUnion::inline((), value)
        },
        Token::Function(ref name) => {
            let function = CalcNode::math_function(context, name, location)?;
            let number = CalcNode::parse_number(context, input, clamping_mode, function)?;
            NumericUnion::boxed(Box::new(number))
        },
        ref t => return Err(location.new_unexpected_token_error(t.clone())),
    }))
}

/// Parse an `<integer>` value, with a given clamping mode.
pub fn parse_integer_with_clamping_mode<'i, 't>(
    context: &ParserContext,
    input: &mut Parser<'i, 't>,
    clamping_mode: AllowedNumericType,
) -> Result<Integer, ParseError<'i>> {
    let location = input.current_source_location();
    Ok(Integer(match *input.next()? {
        Token::Number {
            int_value: Some(v), ..
        } if clamping_mode.is_ok(context.parsing_mode, v as f32) => NumericUnion::inline((), v),
        Token::Function(ref name) => {
            let function = CalcNode::math_function(context, name, location)?;
            let calc = CalcNode::parse_number(context, input, clamping_mode, function)?;
            NumericUnion::boxed(Box::new(calc))
        },
        ref t => return Err(location.new_unexpected_token_error(t.clone())),
    }))
}

/// A non-calc `<number>` value.
#[derive(Clone, Copy, Debug, MallocSizeOf, ToShmem, ToTyped)]
#[repr(C)]
pub struct NoCalcNumber(CSSFloat);

impl NoCalcNumber {
    /// Returns a new literal number with the value `val`.
    #[inline]
    pub fn new(val: CSSFloat) -> Self {
        Self(val)
    }

    /// Returns the raw, underlying value of this number.
    #[inline]
    pub fn value(&self) -> f32 {
        self.0
    }

    /// Returns the numeric value, clamped if needed.
    #[inline]
    pub fn get(&self) -> f32 {
        crate::values::normalize(self.0).min(f32::MAX).max(f32::MIN)
    }

    /// Returns the unit string for a number value.
    pub fn unit(&self) -> &'static str {
        "number"
    }

    /// Returns the canonical unit for a number value (none).
    pub fn canonical_unit(&self) -> Option<&'static str> {
        None
    }

    /// Converts to the given unit, only succeeding if the unit is "number".
    pub fn to(&self, unit: &str) -> Result<Self, ()> {
        if !unit.eq_ignore_ascii_case("number") {
            return Err(());
        }
        Ok(self.clone())
    }
}

impl PartialEq<NoCalcNumber> for NoCalcNumber {
    fn eq(&self, other: &NoCalcNumber) -> bool {
        self.0 == other.0 || (self.0.is_nan() && other.0.is_nan())
    }
}

impl PartialOrd<NoCalcNumber> for NoCalcNumber {
    fn partial_cmp(&self, other: &NoCalcNumber) -> Option<std::cmp::Ordering> {
        self.get().partial_cmp(&other.get())
    }
}

impl ToCss for NoCalcNumber {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        serialize_number(self.0, dest)
    }
}

impl ToComputedValue for NoCalcNumber {
    type ComputedValue = CSSFloat;

    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        self.get()
    }

    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        Self::new(*computed)
    }
}

/// A CSS `<number>` specified value.
///
/// https://drafts.csswg.org/css-values-3/#number-value
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
pub struct Number(NumericUnion<(), f32, CalcNumeric>);

impl ToCss for Number {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        match self.0.unpack() {
            Unpacked::Inline(_, v) => NoCalcNumber(v).to_css(dest),
            Unpacked::Boxed(calc) => calc.to_css(dest),
        }
    }
}

impl ToTyped for Number {
    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
        match self.0.unpack() {
            Unpacked::Inline((), v) => NoCalcNumber(v).to_typed(dest),
            Unpacked::Boxed(ref calc) => calc.to_typed(dest),
        }
    }
}

impl Parse for Number {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        parse_number_with_clamping_mode(context, input, AllowedNumericType::All)
    }
}

impl PartialOrd<Number> for Number {
    fn partial_cmp(&self, other: &Number) -> Option<std::cmp::Ordering> {
        self.get().partial_cmp(&other.get())
    }
}

impl Number {
    /// Returns a new number with the value `val`.
    #[inline]
    pub fn new(val: CSSFloat) -> Self {
        Self(NumericUnion::inline((), val))
    }

    /// Returns a new number with the value `val`.
    #[inline]
    pub fn new_calc(val: Box<CalcNumeric>) -> Self {
        Self(NumericUnion::boxed(val))
    }

    /// Returns this number as a percentage.
    pub fn to_percentage(&self) -> Option<Percentage> {
        Some(match self.0.unpack() {
            Unpacked::Inline((), n) => Percentage::new(n),
            Unpacked::Boxed(ref calc) => {
                let n = calc.as_number()?.get();
                Percentage::new_calc(Box::new(
                    calc.with_leaf_node(Leaf::Percentage(NoCalcPercentage::new(n))),
                ))
            },
        })
    }

    /// Returns the value if this is a plain (non-calc) number, or None otherwise.
    /// Use `resolve()` to also handle resolvable calc expressions, or `to_computed_value()`
    /// when computed context is available.
    #[inline]
    pub fn get(&self) -> Option<f32> {
        match self.0.unpack() {
            Unpacked::Inline((), f) => Some(NoCalcNumber(f).get()),
            Unpacked::Boxed(..) => None,
        }
    }

    /// Returns the value if it can be resolved at parse time, including resolvable calc
    /// expressions. Returns None for calc expressions that require computed-value context.
    pub fn resolve(&self) -> Option<f32> {
        match self.0.unpack() {
            Unpacked::Inline((), f) => Some(NoCalcNumber(f).get()),
            Unpacked::Boxed(ref calc) => calc.as_number().map(|n| n.get()),
        }
    }

    #[allow(missing_docs)]
    pub fn parse_non_negative<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Number, ParseError<'i>> {
        parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
    }

    #[allow(missing_docs)]
    pub fn parse_at_least_one<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Number, ParseError<'i>> {
        parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
    }

    /// Clamp to 1.0 if the value is over 1.0.
    #[inline]
    pub fn clamp_to_one(&mut self) {
        match self.0.unpack_mut() {
            UnpackedMut::Inline(_, ref mut v) => **v = v.min(1.),
            UnpackedMut::Boxed(ref mut calc) => {
                calc.clamping_mode = AllowedNumericType::ZeroToOne;
            },
        }
    }
}

impl ToComputedValue for Number {
    type ComputedValue = CSSFloat;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> CSSFloat {
        match self.0.unpack() {
            Unpacked::Inline((), n) => NoCalcNumber(n).to_computed_value(context),
            Unpacked::Boxed(ref calc) => {
                let value = calc.resolve(context, |result| match result {
                    Ok(Leaf::Number(n)) => n.get(),
                    _ => {
                        debug_assert!(false"Unexpected Number::Calc without resolved number");
                        f32::NAN
                    },
                });
                crate::values::normalize(value).min(f32::MAX).max(f32::MIN)
            },
        }
    }

    #[inline]
    fn from_computed_value(computed: &CSSFloat) -> Self {
        Number::new(*computed)
    }
}

impl IsParallelTo for (Number, Number, Number) {
    fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
        use euclid::approxeq::ApproxEq;
        // If a and b is parallel, the angle between them is 0deg, so
        // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
        match (self.0.get(), self.1.get(), self.2.get()) {
            (Some(x), Some(y), Some(z)) => DirectionVector::new(x, y, z)
                .cross(*vector)
                .square_length()
                .approx_eq(&0.0f32),
            _ => false,
        }
    }
}

impl SpecifiedValueInfo for Number {}

impl Zero for Number {
    #[inline]
    fn zero() -> Self {
        Self::new(0.)
    }

    // Returns true if this number was a non-calc 0.
    #[inline]
    fn is_zero(&self) -> bool {
        self.get() == Some(0.)
    }
}

/// A Number which is >= 0.0.
pub type NonNegativeNumber = NonNegative<Number>;

impl Parse for NonNegativeNumber {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
            .map(NonNegative::<Number>)
    }
}

impl One for NonNegativeNumber {
    #[inline]
    fn one() -> Self {
        NonNegativeNumber::new(1.0)
    }

    // Returns true if this number was a non-calc 1.
    #[inline]
    fn is_one(&self) -> bool {
        self.get() == Some(1.)
    }
}

impl NonNegativeNumber {
    /// Returns a new non-negative number with the value `val`.
    pub fn new(val: CSSFloat) -> Self {
        NonNegative(Number::new(val.max(0.)))
    }

    /// Returns the numeric value.
    #[inline]
    pub fn get(&self) -> Option<f32> {
        self.0.get()
    }
}

/// An Integer which is >= 0. For calc expressions that couldn't be resolved at parse time,
/// this value is clamped to 0 at computed-value time.
pub type NonNegativeInteger = NonNegative<Integer>;

impl Parse for NonNegativeInteger {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Ok(NonNegative(Integer::parse_non_negative(context, input)?))
    }
}

/// A Number which is >= 1.0.
pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<Number>;

impl Parse for GreaterThanOrEqualToOneNumber {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
            .map(GreaterThanOrEqualToOne::<Number>)
    }
}

/// A specified `<integer>`, either a simple integer value, a resolved calc expression,
/// or a full calc expression tree that cannot be computed at parse time.
/// Note that a calc expression may not actually be an integer; it will be rounded
/// at computed-value time.
///
/// <https://drafts.csswg.org/css-values/#integers>
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
pub struct Integer(NumericUnion<(), i32, CalcNumeric>);

impl Zero for Integer {
    #[inline]
    fn zero() -> Self {
        Self::new(0)
    }

    // Returns true if this integer was a non-calc 0.
    #[inline]
    fn is_zero(&self) -> bool {
        self.get() == Some(0)
    }
}

impl One for Integer {
    #[inline]
    fn one() -> Self {
        Self::new(1)
    }

    // Returns true if this integer was a non-calc 1.
    #[inline]
    fn is_one(&self) -> bool {
        self.get() == Some(1)
    }
}

impl PartialEq<i32> for Integer {
    fn eq(&self, value: &i32) -> bool {
        self.get().is_some_and(|v| v == *value)
    }
}

impl Integer {
    /// Trivially constructs a new `Integer` value.
    pub fn new(val: CSSInteger) -> Self {
        Self(NumericUnion::inline((), val))
    }

    /// Returns the value if this is a plain (non-calc) integer, or None otherwise.
    /// Use `resolve()` to also handle resolvable calc expressions, or `to_computed_value()`
    /// when computed context is available.
    pub fn get(&self) -> Option<CSSInteger> {
        match self.0.unpack() {
            Unpacked::Inline((), v) => Some(v),
            Unpacked::Boxed(..) => None,
        }
    }

    /// Returns the value if it can be resolved at parse time, including resolvable calc
    /// expressions. Returns None for calc expressions that require computed-value context.
    pub fn resolve(&self) -> Option<CSSInteger> {
        Some(match self.0.unpack() {
            Unpacked::Inline((), v) => v,
            Unpacked::Boxed(ref calc) => {
                let value = calc.as_number()?.get();
                (value + 0.5).floor() as CSSInteger
            },
        })
    }

    /// Makes sure this number matches the clamping, or errors otherwise.
    pub fn ensure_clamping_mode(&mut self, clamping_mode: AllowedNumericType) -> Result<(), ()> {
        match self.0.unpack_mut() {
            UnpackedMut::Inline(_, i) => {
                if !clamping_mode.is_ok(ParsingMode::DEFAULT, *i as f32) {
                    return Err(());
                }
            },
            UnpackedMut::Boxed(ref mut calc) => {
                calc.clamping_mode = clamping_mode;
            },
        }
        Ok(())
    }
}

impl Parse for Integer {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        parse_integer_with_clamping_mode(context, input, AllowedNumericType::All)
    }
}

impl Integer {
    /// Parse a non-negative integer.
    pub fn parse_non_negative<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Integer, ParseError<'i>> {
        parse_integer_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
    }

    /// Parse a positive integer (>= 1).
    pub fn parse_positive<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Integer, ParseError<'i>> {
        parse_integer_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
    }
}

impl ToCss for Integer {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        match self.0.unpack() {
            Unpacked::Inline(_, v) => v.to_css(dest),
            Unpacked::Boxed(calc) => calc.to_css(dest),
        }
    }
}

impl ToTyped for Integer {
    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
        match self.0.unpack() {
            Unpacked::Inline((), n) => n.to_typed(dest),
            Unpacked::Boxed(ref calc) => calc.to_typed(dest),
        }
    }
}

impl ToComputedValue for Integer {
    type ComputedValue = i32;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> i32 {
        match self.0.unpack() {
            Unpacked::Inline((), i) => i,
            Unpacked::Boxed(ref calc) => {
                let value = calc.resolve(context, |result| match result {
                    Ok(Leaf::Number(n)) => n.get(),
                    _ => {
                        debug_assert!(false"Unexpected Integer::Calc without resolved number");
                        f32::NAN
                    },
                });
                let clamped = crate::values::normalize(value).min(f32::MAX).max(f32::MIN);
                (clamped + 0.5).floor() as i32
            },
        }
    }

    #[inline]
    fn from_computed_value(computed: &i32) -> Self {
        Self::new(*computed)
    }
}

impl SpecifiedValueInfo for Integer {}

/// An Integer which is >= 1. For calc expressions that couldn't be resolved at parse time,
/// this value is clamped to 1 at computed-value time.
pub type PositiveInteger = GreaterThanOrEqualToOne<Integer>;

impl Parse for PositiveInteger {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Integer::parse_positive(context, input).map(GreaterThanOrEqualToOne)
    }
}

Messung V0.5 in Prozent
C=91 H=100 G=95

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.