3
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
4
5
5
use dtoa_short:: { self , Notation } ;
6
+ use itoa;
6
7
use std:: ascii:: AsciiExt ;
7
8
use std:: fmt:: { self , Write } ;
9
+ use std:: io;
8
10
use std:: str;
9
11
10
12
use super :: Token ;
@@ -24,23 +26,6 @@ pub trait ToCss {
24
26
self . to_css ( & mut s) . unwrap ( ) ;
25
27
s
26
28
}
27
-
28
- /// Serialize `self` in CSS syntax and return a result compatible with `std::fmt::Show`.
29
- ///
30
- /// Typical usage is, for a `Foo` that implements `ToCss`:
31
- ///
32
- /// ```{rust,ignore}
33
- /// use std::fmt;
34
- /// impl fmt::Show for Foo {
35
- /// #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_to_css(f) }
36
- /// }
37
- /// ```
38
- ///
39
- /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
40
- #[ inline]
41
- fn fmt_to_css < W > ( & self , dest : & mut W ) -> fmt:: Result where W : fmt:: Write {
42
- self . to_css ( dest) . map_err ( |_| fmt:: Error )
43
- }
44
29
}
45
30
46
31
#[ inline]
@@ -90,7 +75,7 @@ impl<'a> ToCss for Token<'a> {
90
75
serialize_unquoted_url ( & * * value, dest) ?;
91
76
dest. write_str ( ")" ) ?;
92
77
} ,
93
- Token :: Delim ( value) => write ! ( dest, "{}" , value) ?,
78
+ Token :: Delim ( value) => dest. write_char ( value) ?,
94
79
95
80
Token :: Number { value, int_value, has_sign } => {
96
81
write_numeric ( value, int_value, has_sign, dest) ?
@@ -112,7 +97,11 @@ impl<'a> ToCss for Token<'a> {
112
97
} ,
113
98
114
99
Token :: WhiteSpace ( content) => dest. write_str ( content) ?,
115
- Token :: Comment ( content) => write ! ( dest, "/*{}*/" , content) ?,
100
+ Token :: Comment ( content) => {
101
+ dest. write_str ( "/*" ) ?;
102
+ dest. write_str ( content) ?;
103
+ dest. write_str ( "*/" ) ?
104
+ }
116
105
Token :: Colon => dest. write_str ( ":" ) ?,
117
106
Token :: Semicolon => dest. write_str ( ";" ) ?,
118
107
Token :: Comma => dest. write_str ( "," ) ?,
@@ -143,6 +132,32 @@ impl<'a> ToCss for Token<'a> {
143
132
}
144
133
}
145
134
135
+ fn to_hex_byte ( value : u8 ) -> u8 {
136
+ match value {
137
+ 0 ...9 => value + b'0' ,
138
+ _ => value - 10 + b'a' ,
139
+ }
140
+ }
141
+
142
+ fn hex_escape < W > ( ascii_byte : u8 , dest : & mut W ) -> fmt:: Result where W : fmt:: Write {
143
+ let high = ascii_byte >> 4 ;
144
+ let b3;
145
+ let b4;
146
+ let bytes = if high > 0 {
147
+ let low = ascii_byte & 0x0F ;
148
+ b4 = [ b'\\' , to_hex_byte ( high) , to_hex_byte ( low) , b' ' ] ;
149
+ & b4[ ..]
150
+ } else {
151
+ b3 = [ b'\\' , to_hex_byte ( ascii_byte) , b' ' ] ;
152
+ & b3[ ..]
153
+ } ;
154
+ dest. write_str ( unsafe { str:: from_utf8_unchecked ( & bytes) } )
155
+ }
156
+
157
+ fn char_escape < W > ( ascii_byte : u8 , dest : & mut W ) -> fmt:: Result where W : fmt:: Write {
158
+ let bytes = [ b'\\' , ascii_byte] ;
159
+ dest. write_str ( unsafe { str:: from_utf8_unchecked ( & bytes) } )
160
+ }
146
161
147
162
/// Write a CSS identifier, escaping characters as necessary.
148
163
pub fn serialize_identifier < W > ( mut value : & str , dest : & mut W ) -> fmt:: Result where W : fmt:: Write {
@@ -161,7 +176,7 @@ pub fn serialize_identifier<W>(mut value: &str, dest: &mut W) -> fmt::Result whe
161
176
value = & value[ 1 ..] ;
162
177
}
163
178
if let digit @ b'0' ...b'9' = value. as_bytes ( ) [ 0 ] {
164
- write ! ( dest , " \\ 3{} " , digit as char ) ?;
179
+ hex_escape ( digit , dest ) ?;
165
180
value = & value[ 1 ..] ;
166
181
}
167
182
serialize_name ( value, dest)
@@ -182,9 +197,9 @@ fn serialize_name<W>(value: &str, dest: &mut W) -> fmt::Result where W:fmt::Writ
182
197
if let Some ( escaped) = escaped {
183
198
dest. write_str ( escaped) ?;
184
199
} else if ( b >= b'\x01' && b <= b'\x1F' ) || b == b'\x7F' {
185
- write ! ( dest , " \\ {:x} " , b ) ?;
200
+ hex_escape ( b , dest ) ?;
186
201
} else {
187
- write ! ( dest , " \\ {}" , b as char ) ?;
202
+ char_escape ( b , dest ) ?;
188
203
}
189
204
chunk_start = i + 1 ;
190
205
}
@@ -202,9 +217,9 @@ fn serialize_unquoted_url<W>(value: &str, dest: &mut W) -> fmt::Result where W:f
202
217
} ;
203
218
dest. write_str ( & value[ chunk_start..i] ) ?;
204
219
if hex {
205
- write ! ( dest , " \\ {:X} " , b ) ?;
220
+ hex_escape ( b , dest ) ?;
206
221
} else {
207
- write ! ( dest , " \\ {}" , b as char ) ?;
222
+ char_escape ( b , dest ) ?;
208
223
}
209
224
chunk_start = i + 1 ;
210
225
}
@@ -262,7 +277,7 @@ impl<'a, W> fmt::Write for CssStringWriter<'a, W> where W: fmt::Write {
262
277
self . inner . write_str ( & s[ chunk_start..i] ) ?;
263
278
match escaped {
264
279
Some ( x) => self . inner . write_str ( x) ?,
265
- None => write ! ( self . inner, " \\ {:x} " , b ) ?,
280
+ None => hex_escape ( b , self . inner ) ?,
266
281
} ;
267
282
chunk_start = i + 1 ;
268
283
}
@@ -275,7 +290,33 @@ macro_rules! impl_tocss_for_int {
275
290
( $T: ty) => {
276
291
impl <' a> ToCss for $T {
277
292
fn to_css<W >( & self , dest: & mut W ) -> fmt:: Result where W : fmt:: Write {
278
- write!( dest, "{}" , * self )
293
+ struct AssumeUtf8 <W : fmt:: Write >( W ) ;
294
+
295
+ impl <W : fmt:: Write > io:: Write for AssumeUtf8 <W > {
296
+ #[ inline]
297
+ fn write_all( & mut self , buf: & [ u8 ] ) -> io:: Result <( ) > {
298
+ // Safety: itoa only emits ASCII, which is also well-formed UTF-8.
299
+ debug_assert!( buf. is_ascii( ) ) ;
300
+ self . 0 . write_str( unsafe { str :: from_utf8_unchecked( buf) } )
301
+ . map_err( |_| io:: ErrorKind :: Other . into( ) )
302
+ }
303
+
304
+ #[ inline]
305
+ fn write( & mut self , buf: & [ u8 ] ) -> io:: Result <usize > {
306
+ self . write_all( buf) ?;
307
+ Ok ( buf. len( ) )
308
+ }
309
+
310
+ #[ inline]
311
+ fn flush( & mut self ) -> io:: Result <( ) > {
312
+ Ok ( ( ) )
313
+ }
314
+ }
315
+
316
+ match itoa:: write( AssumeUtf8 ( dest) , * self ) {
317
+ Ok ( _) => Ok ( ( ) ) ,
318
+ Err ( _) => Err ( fmt:: Error )
319
+ }
279
320
}
280
321
}
281
322
}
0 commit comments