Handle compiler panics when handling infinite lengths (#2215)

This commit is contained in:
bluebear94 2023-09-28 05:15:09 -04:00 committed by GitHub
parent ffcd951bc8
commit 553da642bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 98 additions and 48 deletions

View File

@ -738,12 +738,12 @@ pub struct Paper {
impl Paper {
/// The width of the paper.
pub fn width(self) -> Abs {
Abs::mm(self.width.0)
Abs::mm(self.width.get())
}
/// The height of the paper.
pub fn height(self) -> Abs {
Abs::mm(self.height.0)
Abs::mm(self.height.get())
}
}
@ -756,8 +756,8 @@ macro_rules! papers {
impl Paper {
$(pub const $var: Self = Self {
name: $name,
width: Scalar($width),
height: Scalar($height),
width: Scalar::new($width),
height: Scalar::new($height),
};)*
}

View File

@ -34,7 +34,9 @@ pub(crate) fn field(value: &Value, field: &str) -> StrResult<Value> {
"cap" => stroke.line_cap.into_value(),
"join" => stroke.line_join.into_value(),
"dash" => stroke.dash_pattern.clone().into_value(),
"miter-limit" => stroke.miter_limit.map(|limit| limit.0).into_value(),
"miter-limit" => {
stroke.miter_limit.map(|limit| limit.get()).into_value()
}
_ => return missing(),
}
} else if let Some(align) = dynamic.downcast::<Align>() {

View File

@ -336,7 +336,7 @@ impl PageContext<'_, '_> {
}
}
if self.state.stroke.as_ref().map(|s| &s.miter_limit) != Some(miter_limit) {
self.content.set_miter_limit(miter_limit.0 as f32);
self.content.set_miter_limit(miter_limit.get() as f32);
}
self.state.stroke = Some(stroke.clone());
}

View File

@ -496,7 +496,7 @@ fn render_shape(
line_cap: line_cap.into(),
line_join: line_join.into(),
dash,
miter_limit: miter_limit.0 as f32,
miter_limit: miter_limit.get() as f32,
};
canvas.stroke_path(&path, &paint, &stroke, ts, mask);
}

View File

@ -323,7 +323,8 @@ impl SVGRenderer {
LineJoin::Bevel => "bevel",
},
);
self.xml.write_attribute("stroke-miterlimit", &stroke.miter_limit.0);
self.xml
.write_attribute("stroke-miterlimit", &stroke.miter_limit.get());
if let Some(pattern) = &stroke.dash_pattern {
self.xml.write_attribute("stroke-dashoffset", &pattern.phase.to_pt());
self.xml.write_attribute(

View File

@ -7,22 +7,22 @@ pub struct Abs(Scalar);
impl Abs {
/// The zero length.
pub const fn zero() -> Self {
Self(Scalar(0.0))
Self(Scalar::ZERO)
}
/// The infinite length.
pub const fn inf() -> Self {
Self(Scalar(f64::INFINITY))
Self(Scalar::INFINITY)
}
/// Create an absolute length from a number of raw units.
pub const fn raw(raw: f64) -> Self {
Self(Scalar(raw))
Self(Scalar::new(raw))
}
/// Create an absolute length from a value in a unit.
pub fn with_unit(val: f64, unit: AbsUnit) -> Self {
Self(Scalar(val * unit.raw_scale()))
Self(Scalar::new(val * unit.raw_scale()))
}
/// Create an absolute length from a number of points.
@ -47,7 +47,7 @@ impl Abs {
/// Get the value of this absolute length in raw units.
pub const fn to_raw(self) -> f64 {
(self.0).0
(self.0).get()
}
/// Get the value of this absolute length in a unit.

View File

@ -18,17 +18,17 @@ pub struct Angle(Scalar);
impl Angle {
/// The zero angle.
pub const fn zero() -> Self {
Self(Scalar(0.0))
Self(Scalar::ZERO)
}
/// Create an angle from a number of raw units.
pub const fn raw(raw: f64) -> Self {
Self(Scalar(raw))
Self(Scalar::new(raw))
}
/// Create an angle from a value in a unit.
pub fn with_unit(val: f64, unit: AngleUnit) -> Self {
Self(Scalar(val * unit.raw_scale()))
Self(Scalar::new(val * unit.raw_scale()))
}
/// Create an angle from a number of radians.
@ -43,7 +43,7 @@ impl Angle {
/// Get the value of this angle in raw units.
pub const fn to_raw(self) -> f64 {
(self.0).0
(self.0).get()
}
/// Get the value of this angle in a unit.

View File

@ -9,29 +9,29 @@ pub struct Em(Scalar);
impl Em {
/// The zero em length.
pub const fn zero() -> Self {
Self(Scalar(0.0))
Self(Scalar::ZERO)
}
/// The font size.
pub const fn one() -> Self {
Self(Scalar(1.0))
Self(Scalar::ONE)
}
/// Create a font-relative length.
pub const fn new(em: f64) -> Self {
Self(Scalar(em))
Self(Scalar::new(em))
}
/// Create an em length from font units at the given units per em.
pub fn from_units(units: impl Into<f64>, units_per_em: f64) -> Self {
Self(Scalar(units.into() / units_per_em))
Self(Scalar::new(units.into() / units_per_em))
}
/// Create an em length from a length at the given font size.
pub fn from_length(length: Abs, font_size: Abs) -> Self {
let result = length / font_size;
if result.is_finite() {
Self(Scalar(result))
Self(Scalar::new(result))
} else {
Self::zero()
}
@ -39,7 +39,7 @@ impl Em {
/// The number of em units.
pub const fn get(self) -> f64 {
(self.0).0
(self.0).get()
}
/// The absolute value of this em length.

View File

@ -19,22 +19,22 @@ pub struct Fr(Scalar);
impl Fr {
/// Takes up zero space: `0fr`.
pub const fn zero() -> Self {
Self(Scalar(0.0))
Self(Scalar::ZERO)
}
/// Takes up as much space as all other items with this fraction: `1fr`.
pub const fn one() -> Self {
Self(Scalar(1.0))
Self(Scalar::ONE)
}
/// Create a new fraction.
pub const fn new(ratio: f64) -> Self {
Self(Scalar(ratio))
Self(Scalar::new(ratio))
}
/// Get the underlying number.
pub const fn get(self) -> f64 {
(self.0).0
(self.0).get()
}
/// The absolute value of this fraction.

View File

@ -18,22 +18,22 @@ pub struct Ratio(Scalar);
impl Ratio {
/// A ratio of `0%` represented as `0.0`.
pub const fn zero() -> Self {
Self(Scalar(0.0))
Self(Scalar::ZERO)
}
/// A ratio of `100%` represented as `1.0`.
pub const fn one() -> Self {
Self(Scalar(1.0))
Self(Scalar::ONE)
}
/// Create a new ratio from a value, where `1.0` means `100%`.
pub const fn new(ratio: f64) -> Self {
Self(Scalar(ratio))
Self(Scalar::new(ratio))
}
/// Get the underlying ratio.
pub const fn get(self) -> f64 {
(self.0).0
(self.0).get()
}
/// Whether the ratio is zero.

View File

@ -4,7 +4,40 @@ use super::*;
///
/// Panics if it's `NaN` during any of those operations.
#[derive(Default, Copy, Clone)]
pub struct Scalar(pub f64);
pub struct Scalar(f64);
// We have to detect NaNs this way since `f64::is_nan` isnt const
// on stable yet:
// ([tracking issue](https://github.com/rust-lang/rust/issues/57241))
#[allow(clippy::unusual_byte_groupings)]
const fn is_nan_const(x: f64) -> bool {
// Safety: all bit patterns are valid for u64, and f64 has no padding bits.
// We cannot use `f64::to_bits` because it is not const.
let x_bits = unsafe { std::mem::transmute::<f64, u64>(x) };
(x_bits << 1 >> (64 - 12 + 1)) == 0b0_111_1111_1111 && (x_bits << 12) != 0
}
impl Scalar {
/// Creates a [`Scalar`] with the given value.
///
/// If the value is NaN, then it is set to `0.0` in the result.
pub const fn new(x: f64) -> Self {
Self(if is_nan_const(x) { 0.0 } else { x })
}
/// Gets the value of this [`Scalar`].
#[inline]
pub const fn get(self) -> f64 {
self.0
}
/// The scalar containing `0.0`.
pub const ZERO: Self = Self(0.0);
/// The scalar containing `1.0`.
pub const ONE: Self = Self(1.0);
/// The scalar containing `f64::INFINITY`.
pub const INFINITY: Self = Self(f64::INFINITY);
}
impl Numeric for Scalar {
fn zero() -> Self {
@ -18,7 +51,7 @@ impl Numeric for Scalar {
impl From<f64> for Scalar {
fn from(float: f64) -> Self {
Self(float)
Self::new(float)
}
}
@ -88,7 +121,7 @@ impl Neg for Scalar {
type Output = Self;
fn neg(self) -> Self::Output {
Self(-self.0)
Self::new(-self.0)
}
}
@ -96,13 +129,13 @@ impl<T: Into<Self>> Add<T> for Scalar {
type Output = Self;
fn add(self, rhs: T) -> Self::Output {
Self(self.0 + rhs.into().0)
Self::new(self.0 + rhs.into().0)
}
}
impl<T: Into<Self>> AddAssign<T> for Scalar {
fn add_assign(&mut self, rhs: T) {
self.0 += rhs.into().0;
*self = *self + rhs.into();
}
}
@ -110,13 +143,13 @@ impl<T: Into<Self>> Sub<T> for Scalar {
type Output = Self;
fn sub(self, rhs: T) -> Self::Output {
Self(self.0 - rhs.into().0)
Self::new(self.0 - rhs.into().0)
}
}
impl<T: Into<Self>> SubAssign<T> for Scalar {
fn sub_assign(&mut self, rhs: T) {
self.0 -= rhs.into().0;
*self = *self - rhs.into();
}
}
@ -124,13 +157,13 @@ impl<T: Into<Self>> Mul<T> for Scalar {
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
Self(self.0 * rhs.into().0)
Self::new(self.0 * rhs.into().0)
}
}
impl<T: Into<Self>> MulAssign<T> for Scalar {
fn mul_assign(&mut self, rhs: T) {
self.0 *= rhs.into().0;
*self = *self * rhs.into();
}
}
@ -138,13 +171,13 @@ impl<T: Into<Self>> Div<T> for Scalar {
type Output = Self;
fn div(self, rhs: T) -> Self::Output {
Self(self.0 / rhs.into().0)
Self::new(self.0 / rhs.into().0)
}
}
impl<T: Into<Self>> DivAssign<T> for Scalar {
fn div_assign(&mut self, rhs: T) {
self.0 /= rhs.into().0;
*self = *self / rhs.into();
}
}
@ -152,24 +185,24 @@ impl<T: Into<Self>> Rem<T> for Scalar {
type Output = Self;
fn rem(self, rhs: T) -> Self::Output {
Self(self.0 % rhs.into().0)
Self::new(self.0 % rhs.into().0)
}
}
impl<T: Into<Self>> RemAssign<T> for Scalar {
fn rem_assign(&mut self, rhs: T) {
self.0 %= rhs.into().0;
*self = *self % rhs.into();
}
}
impl Sum for Scalar {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
Self(iter.map(|s| s.0).sum())
Self::new(iter.map(|s| s.0).sum())
}
}
impl<'a> Sum<&'a Self> for Scalar {
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
Self(iter.map(|s| s.0).sum())
Self::new(iter.map(|s| s.0).sum())
}
}

View File

@ -266,7 +266,7 @@ cast! {
line_cap,
line_join,
dash_pattern,
miter_limit: miter_limit.map(Scalar),
miter_limit: miter_limit.map(Scalar::new),
}
},
}
@ -460,7 +460,7 @@ impl Default for FixedStroke {
line_cap: LineCap::Butt,
line_join: LineJoin::Miter,
dash_pattern: None,
miter_limit: Scalar(4.0),
miter_limit: Scalar::new(4.0),
}
}
}

View File

@ -128,6 +128,20 @@
// Error: 2-8 invalid hexadecimal number: 0x123z
#0x123z
---
// Test that multiplying infinite numbers by certain units does not crash.
#(float("inf") * 1pt)
#(float("inf") * 1em)
#(float("inf") * (1pt + 1em))
---
// Test that trying to produce a NaN scalar (such as in lengths) does not crash.
#let infpt = float("inf") * 1pt
#test(infpt - infpt, 0pt)
#test(infpt + (-infpt), 0pt)
// TODO: this result is surprising
#test(infpt / float("inf"), 0pt)
---
// Test boolean operators.