From 2181f2f33aac76ff17dec5a5b53aa9c4989824e6 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Mon, 24 Jul 2017 17:01:31 +1000 Subject: [PATCH] Serialize number to no more than necessary precision --- Cargo.toml | 3 +- .../component_value_list.json | 12 +++-- src/lib.rs | 1 + src/serializer.rs | 48 ++++++++++++------- src/tests.rs | 24 +++++++++- 5 files changed, 66 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a851b5f7..b259cd99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser" -version = "0.19.1" +version = "0.19.2" authors = [ "Simon Sapin " ] description = "Rust implementation of CSS Syntax Level 3" @@ -20,6 +20,7 @@ encoding_rs = "0.5" [dependencies] cssparser-macros = {path = "./macros", version = "0.3"} +dtoa-short = "0.3" heapsize = {version = ">= 0.3, < 0.5", optional = true} matches = "0.1" phf = "0.7" diff --git a/src/css-parsing-tests/component_value_list.json b/src/css-parsing-tests/component_value_list.json index 9ae0c48b..aa5354ef 100644 --- a/src/css-parsing-tests/component_value_list.json +++ b/src/css-parsing-tests/component_value_list.json @@ -247,6 +247,12 @@ ["number", "-0.67", -0.67, "number"] ], +"12.3e30 -0.0123e30 9999e-13", [ + ["number", "1.23e31", 1.23e31, "number"], " ", + ["number", "-1.23e28", -1.23e28, "number"], " ", + ["number", "9.999e-10", 9.999e-10, "number"] +], + "3. /* Decimal point must have following digits */", [ ["number", "3", 3, "integer"], ".", " " ], @@ -264,12 +270,12 @@ ["percentage", "12", 12, "integer"], " ", ["percentage", "+34", 34, "integer"], " ", ["percentage", "-45", -45, "integer"], " ", - ["percentage", "0.66999996", 0.67, "number"], " ", + ["percentage", "0.67", 0.67, "number"], " ", ["percentage", "+0.89", 0.89, "number"], " ", ["percentage", "-0.01", -0.01, "number"], " ", ["percentage", "2.3", 2.3000000000, "number"], " ", ["percentage", "+45.0", 45.0, "number"], " ", - ["percentage", "-0.66999996", -0.67, "number"] + ["percentage", "-0.67", -0.67, "number"] ], "12e2% +34e+1% -45E-0% .68e+3% +.79e-1% -.01E2% 2.3E+1% +45.0e6% -0.67e0%", [ @@ -281,7 +287,7 @@ ["percentage", "-1.0", -1, "number"], " ", ["percentage", "23.0", 23, "number"], " ", ["percentage", "+45000000.0", 45000000, "number"], " ", - ["percentage", "-0.66999996", -0.67, "number"] + ["percentage", "-0.67", -0.67, "number"] ], "12\\% /* Percent sign can not be escaped */", [ diff --git a/src/lib.rs b/src/lib.rs index b9ccc1c5..693c771a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,6 +68,7 @@ fn parse_border_spacing(_context: &ParserContext, input: &mut Parser) #![recursion_limit="200"] // For color::parse_color_keyword +extern crate dtoa_short; #[macro_use] extern crate cssparser_macros; #[macro_use] extern crate matches; #[macro_use] extern crate procedural_masquerade; diff --git a/src/serializer.rs b/src/serializer.rs index 7347463b..885bc96f 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -2,8 +2,10 @@ * 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/. */ +use dtoa_short::{self, Notation}; use std::ascii::AsciiExt; use std::fmt::{self, Write}; +use std::str; use super::Token; @@ -41,7 +43,6 @@ pub trait ToCss { } } - #[inline] fn write_numeric(value: f32, int_value: Option, has_sign: bool, dest: &mut W) -> fmt::Result where W: fmt::Write { @@ -50,15 +51,18 @@ fn write_numeric(value: f32, int_value: Option, has_sign: bool, dest: &m dest.write_str("+")?; } - if value == 0.0 && value.is_sign_negative() { + let notation = if value == 0.0 && value.is_sign_negative() { // Negative zero. Work around #20596. - dest.write_str("-0")? + dest.write_str("-0")?; + Notation { decimal_point: false, scientific: false } } else { - write!(dest, "{}", value)? - } + dtoa_short::write(dest, value)? + }; if int_value.is_none() && value.fract() == 0. { - dest.write_str(".0")?; + if !notation.decimal_point && !notation.scientific { + dest.write_str(".0")?; + } } Ok(()) } @@ -267,7 +271,7 @@ impl<'a, W> fmt::Write for CssStringWriter<'a, W> where W: fmt::Write { } -macro_rules! impl_tocss_for_number { +macro_rules! impl_tocss_for_int { ($T: ty) => { impl<'a> ToCss for $T { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { @@ -277,17 +281,27 @@ macro_rules! impl_tocss_for_number { } } -impl_tocss_for_number!(f32); -impl_tocss_for_number!(f64); -impl_tocss_for_number!(i8); -impl_tocss_for_number!(u8); -impl_tocss_for_number!(i16); -impl_tocss_for_number!(u16); -impl_tocss_for_number!(i32); -impl_tocss_for_number!(u32); -impl_tocss_for_number!(i64); -impl_tocss_for_number!(u64); +impl_tocss_for_int!(i8); +impl_tocss_for_int!(u8); +impl_tocss_for_int!(i16); +impl_tocss_for_int!(u16); +impl_tocss_for_int!(i32); +impl_tocss_for_int!(u32); +impl_tocss_for_int!(i64); +impl_tocss_for_int!(u64); + +macro_rules! impl_tocss_for_float { + ($T: ty) => { + impl<'a> ToCss for $T { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dtoa_short::write(dest, *self).map(|_| ()) + } + } + } +} +impl_tocss_for_float!(f32); +impl_tocss_for_float!(f64); /// A category of token. See the `needs_separator_when_before` method. #[derive(Copy, Clone, Eq, PartialEq, Debug)] diff --git a/src/tests.rs b/src/tests.rs index 9037bc53..36363453 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -31,7 +31,7 @@ fn almost_equals(a: &Json, b: &Json) -> bool { (_, &Json::I64(b)) => almost_equals(a, &Json::F64(b as f64)), (_, &Json::U64(b)) => almost_equals(a, &Json::F64(b as f64)), - (&Json::F64(a), &Json::F64(b)) => (a - b).abs() < 1e-6, + (&Json::F64(a), &Json::F64(b)) => (a - b).abs() <= a.abs() * 1e-6, (&Json::Boolean(a), &Json::Boolean(b)) => a == b, (&Json::String(ref a), &Json::String(ref b)) => a == b, @@ -1004,3 +1004,25 @@ fn parse_comments() { assert_eq!(parser.current_source_map_url(), test.1); } } + +#[test] +fn roundtrip_percentage_token() { + fn test_roundtrip(value: &str) { + let mut input = ParserInput::new(value); + let mut parser = Parser::new(&mut input); + let token = parser.next().unwrap(); + assert_eq!(token.to_css_string(), value); + } + // Test simple number serialization + for i in 0..101 { + test_roundtrip(&format!("{}%", i)); + for j in 0..10 { + if j != 0 { + test_roundtrip(&format!("{}.{}%", i, j)); + } + for k in 1..10 { + test_roundtrip(&format!("{}.{}{}%", i, j, k)); + } + } + } +}