2
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
4
5
+ use dtoa;
5
6
use std:: ascii:: AsciiExt ;
6
7
use std:: fmt:: { self , Write } ;
8
+ use std:: str;
7
9
8
10
use super :: Token ;
9
11
@@ -41,6 +43,123 @@ pub trait ToCss {
41
43
}
42
44
}
43
45
46
+ const PRECISION_FOR_F32 : usize = 6 ;
47
+
48
+ fn dtoa_pretty < W > ( dest : & mut W , value : f32 , has_exp : & mut bool )
49
+ -> fmt:: Result where W : fmt:: Write {
50
+ let mut buf = [ b'\0' ; 40 ] ;
51
+ // Put a leading zero at the beginning to capture any carry.
52
+ buf[ 0 ] = b'0' ;
53
+ let len = dtoa:: write ( & mut buf[ 1 ..] , value) . unwrap ( ) + 1 ;
54
+ let sign = match buf[ 1 ] {
55
+ s @ b'+' | s @ b'-' => {
56
+ buf[ 1 ] = b'0' ;
57
+ Some ( s)
58
+ }
59
+ _ => None ,
60
+ } ;
61
+ // Locate dot, exp, and the first non-zero digit.
62
+ let mut pos_dot = None ;
63
+ let mut pos_exp = None ;
64
+ let mut prec_start = None ;
65
+ for i in 1 ..len {
66
+ if buf[ i] == b'.' {
67
+ debug_assert ! ( pos_dot. is_none( ) ) ;
68
+ pos_dot = Some ( i) ;
69
+ } else if buf[ i] == b'e' {
70
+ pos_exp = Some ( i) ;
71
+ break ;
72
+ } else if prec_start. is_none ( ) && buf[ i] != b'0' {
73
+ debug_assert ! ( buf[ i] >= b'1' && buf[ i] <= b'9' ) ;
74
+ prec_start = Some ( i) ;
75
+ }
76
+ }
77
+ // If there is no non-zero digit at all, it is just zero.
78
+ if prec_start. is_none ( ) {
79
+ return dest. write_char ( '0' ) ;
80
+ }
81
+ let prec_start = prec_start. unwrap ( ) ;
82
+ let coeff_end = pos_exp. unwrap_or ( len) ;
83
+ let pos_dot = pos_dot. unwrap_or ( coeff_end) ;
84
+ // Find the end position of the number within the given precision.
85
+ let prec_end = {
86
+ let end = prec_start + PRECISION_FOR_F32 ;
87
+ if pos_dot > prec_start && pos_dot <= end {
88
+ end + 1
89
+ } else {
90
+ end
91
+ }
92
+ } ;
93
+ let mut new_coeff_end = coeff_end;
94
+ if prec_end < coeff_end {
95
+ // Round to the given precision.
96
+ let next_char = buf[ prec_end] ;
97
+ new_coeff_end = prec_end;
98
+ if next_char >= b'5' {
99
+ for i in ( 0 ..prec_end) . rev ( ) {
100
+ if buf[ i] == b'.' {
101
+ continue ;
102
+ }
103
+ if buf[ i] != b'9' {
104
+ buf[ i] += 1 ;
105
+ new_coeff_end = i + 1 ;
106
+ break ;
107
+ }
108
+ buf[ i] = b'0' ;
109
+ }
110
+ }
111
+ }
112
+ if new_coeff_end < pos_dot {
113
+ // If the precision isn't enough to reach the dot, set all digits
114
+ // in-between to zero and keep the number until the dot.
115
+ for i in new_coeff_end..pos_dot {
116
+ buf[ i] = b'0' ;
117
+ }
118
+ new_coeff_end = pos_dot;
119
+ } else {
120
+ // Strip any trailing zeros.
121
+ for i in ( 0 ..new_coeff_end) . rev ( ) {
122
+ if buf[ i] != b'0' {
123
+ if buf[ i] == b'.' {
124
+ new_coeff_end = i;
125
+ }
126
+ break ;
127
+ }
128
+ new_coeff_end = i;
129
+ }
130
+ }
131
+ // Move exponent part if necessary.
132
+ let real_end = if let Some ( pos_exp) = pos_exp {
133
+ if new_coeff_end != pos_exp {
134
+ for i in 0 ..len - pos_exp {
135
+ buf[ new_coeff_end + i] = buf[ pos_exp + i] ;
136
+ }
137
+ }
138
+ * has_exp = true ;
139
+ new_coeff_end + ( len - pos_exp)
140
+ } else {
141
+ new_coeff_end
142
+ } ;
143
+ // Add back the sign and strip the leading zero.
144
+ let result = if let Some ( sign) = sign {
145
+ if buf[ 1 ] == b'0' && buf[ 2 ] != b'.' {
146
+ buf[ 1 ] = sign;
147
+ & buf[ 1 ..real_end]
148
+ } else {
149
+ debug_assert ! ( buf[ 0 ] == b'0' ) ;
150
+ buf[ 0 ] = sign;
151
+ & buf[ 0 ..real_end]
152
+ }
153
+ } else {
154
+ if buf[ 1 ] == b'0' && buf[ 2 ] != b'.' {
155
+ & buf[ 2 ..real_end]
156
+ } else {
157
+ debug_assert ! ( buf[ 0 ] == b'0' ) ;
158
+ & buf[ 1 ..real_end]
159
+ }
160
+ } ;
161
+ dest. write_str ( str:: from_utf8 ( result) . unwrap ( ) )
162
+ }
44
163
45
164
#[ inline]
46
165
fn write_numeric < W > ( value : f32 , int_value : Option < i32 > , has_sign : bool , dest : & mut W )
@@ -50,14 +169,15 @@ fn write_numeric<W>(value: f32, int_value: Option<i32>, has_sign: bool, dest: &m
50
169
dest. write_str ( "+" ) ?;
51
170
}
52
171
172
+ let mut has_exp = false ;
53
173
if value == 0.0 && value. is_sign_negative ( ) {
54
174
// Negative zero. Work around #20596.
55
175
dest. write_str ( "-0" ) ?
56
176
} else {
57
- write ! ( dest, "{}" , value ) ?
177
+ dtoa_pretty ( dest, value , & mut has_exp ) ? ;
58
178
}
59
179
60
- if int_value. is_none ( ) && value. fract ( ) == 0. {
180
+ if int_value. is_none ( ) && value. fract ( ) == 0. && !has_exp {
61
181
dest. write_str ( ".0" ) ?;
62
182
}
63
183
Ok ( ( ) )
0 commit comments