33 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44
55use dtoa_short:: { self , Notation } ;
6+ use itoa;
67use std:: ascii:: AsciiExt ;
78use std:: fmt:: { self , Write } ;
9+ use std:: io;
810use std:: str;
911
1012use super :: Token ;
@@ -24,23 +26,6 @@ pub trait ToCss {
2426 self . to_css ( & mut s) . unwrap ( ) ;
2527 s
2628 }
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- }
4429}
4530
4631#[ inline]
@@ -90,7 +75,7 @@ impl<'a> ToCss for Token<'a> {
9075 serialize_unquoted_url ( & * * value, dest) ?;
9176 dest. write_str ( ")" ) ?;
9277 } ,
93- Token :: Delim ( value) => write ! ( dest, "{}" , value) ?,
78+ Token :: Delim ( value) => dest. write_char ( value) ?,
9479
9580 Token :: Number { value, int_value, has_sign } => {
9681 write_numeric ( value, int_value, has_sign, dest) ?
@@ -112,7 +97,11 @@ impl<'a> ToCss for Token<'a> {
11297 } ,
11398
11499 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+ }
116105 Token :: Colon => dest. write_str ( ":" ) ?,
117106 Token :: Semicolon => dest. write_str ( ";" ) ?,
118107 Token :: Comma => dest. write_str ( "," ) ?,
@@ -143,6 +132,32 @@ impl<'a> ToCss for Token<'a> {
143132 }
144133}
145134
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+ }
146161
147162/// Write a CSS identifier, escaping characters as necessary.
148163pub 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
161176 value = & value[ 1 ..] ;
162177 }
163178 if let digit @ b'0' ...b'9' = value. as_bytes ( ) [ 0 ] {
164- write ! ( dest , " \\ 3{} " , digit as char ) ?;
179+ hex_escape ( digit , dest ) ?;
165180 value = & value[ 1 ..] ;
166181 }
167182 serialize_name ( value, dest)
@@ -182,9 +197,9 @@ fn serialize_name<W>(value: &str, dest: &mut W) -> fmt::Result where W:fmt::Writ
182197 if let Some ( escaped) = escaped {
183198 dest. write_str ( escaped) ?;
184199 } else if ( b >= b'\x01' && b <= b'\x1F' ) || b == b'\x7F' {
185- write ! ( dest , " \\ {:x} " , b ) ?;
200+ hex_escape ( b , dest ) ?;
186201 } else {
187- write ! ( dest , " \\ {}" , b as char ) ?;
202+ char_escape ( b , dest ) ?;
188203 }
189204 chunk_start = i + 1 ;
190205 }
@@ -202,9 +217,9 @@ fn serialize_unquoted_url<W>(value: &str, dest: &mut W) -> fmt::Result where W:f
202217 } ;
203218 dest. write_str ( & value[ chunk_start..i] ) ?;
204219 if hex {
205- write ! ( dest , " \\ {:X} " , b ) ?;
220+ hex_escape ( b , dest ) ?;
206221 } else {
207- write ! ( dest , " \\ {}" , b as char ) ?;
222+ char_escape ( b , dest ) ?;
208223 }
209224 chunk_start = i + 1 ;
210225 }
@@ -262,7 +277,7 @@ impl<'a, W> fmt::Write for CssStringWriter<'a, W> where W: fmt::Write {
262277 self . inner . write_str ( & s[ chunk_start..i] ) ?;
263278 match escaped {
264279 Some ( x) => self . inner . write_str ( x) ?,
265- None => write ! ( self . inner, " \\ {:x} " , b ) ?,
280+ None => hex_escape ( b , self . inner ) ?,
266281 } ;
267282 chunk_start = i + 1 ;
268283 }
@@ -275,7 +290,33 @@ macro_rules! impl_tocss_for_int {
275290 ( $T: ty) => {
276291 impl <' a> ToCss for $T {
277292 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+ }
279320 }
280321 }
281322 }
0 commit comments