@@ -42,7 +42,7 @@ extern crate alloc;
4242#[ cfg( feature = "alloc" ) ]
4343use alloc:: { string:: String , vec:: Vec } ;
4444
45- use core:: iter;
45+ use core:: { fmt , iter} ;
4646
4747mod error;
4848pub use crate :: error:: FromHexError ;
@@ -83,55 +83,82 @@ pub trait ToHex {
8383const HEX_CHARS_LOWER : & [ u8 ; 16 ] = b"0123456789abcdef" ;
8484const HEX_CHARS_UPPER : & [ u8 ; 16 ] = b"0123456789ABCDEF" ;
8585
86- struct BytesToHexChars < ' a > {
86+ #[ derive( Clone ) ]
87+ pub struct EncodeHex < ' a > {
8788 inner : :: core:: slice:: Iter < ' a , u8 > ,
8889 table : & ' static [ u8 ; 16 ] ,
8990 next : Option < char > ,
9091}
9192
92- impl < ' a > BytesToHexChars < ' a > {
93- fn new ( inner : & ' a [ u8 ] , table : & ' static [ u8 ; 16 ] ) -> BytesToHexChars < ' a > {
94- BytesToHexChars {
93+ impl < ' a > EncodeHex < ' a > {
94+ #[ inline]
95+ fn new ( inner : & ' a [ u8 ] , table : & ' static [ u8 ; 16 ] ) -> EncodeHex < ' a > {
96+ EncodeHex {
9597 inner : inner. iter ( ) ,
9698 table,
9799 next : None ,
98100 }
99101 }
100102}
101103
102- impl < ' a > Iterator for BytesToHexChars < ' a > {
104+ impl < ' a > Iterator for EncodeHex < ' a > {
103105 type Item = char ;
104106
107+ #[ inline]
105108 fn next ( & mut self ) -> Option < Self :: Item > {
106- match self . next . take ( ) {
107- Some ( current ) => Some ( current ) ,
108- None => self . inner . next ( ) . map ( | byte| {
109- let current = self . table [ ( byte >> 4 ) as usize ] as char ;
110- self . next = Some ( self . table [ ( byte & 0x0F ) as usize ] as char ) ;
109+ self . next . take ( ) . or_else ( || {
110+ self . inner . next ( ) . map ( | & byte| {
111+ let ( high , low ) = byte2hex ( byte, self . table ) ;
112+ let current = high as char ;
113+ self . next = Some ( low as char ) ;
111114 current
112- } ) ,
113- }
115+ } )
116+ } )
114117 }
115118
119+ #[ inline]
116120 fn size_hint ( & self ) -> ( usize , Option < usize > ) {
117121 let length = self . len ( ) ;
118122 ( length, Some ( length) )
119123 }
120124}
121125
122- impl < ' a > iter:: ExactSizeIterator for BytesToHexChars < ' a > {
126+ impl < ' a > iter:: ExactSizeIterator for EncodeHex < ' a > {
127+ #[ inline]
123128 fn len ( & self ) -> usize {
124- let mut length = self . inner . len ( ) * 2 ;
125- if self . next . is_some ( ) {
126- length += 1 ;
129+ self . inner . len ( ) * 2 + self . next . is_some ( ) as usize
130+ }
131+ }
132+
133+ impl fmt:: Display for EncodeHex < ' _ > {
134+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
135+ if f. alternate ( ) {
136+ f. write_str ( "0x" ) ?;
137+ }
138+ if let Some ( c) = self . next {
139+ fmt:: Write :: write_char ( f, c) ?;
140+ }
141+ // write to f in chunks of 64 input bytes at a time
142+ const CHUNK : usize = 64 ;
143+ let mut buf = [ 0u8 ; CHUNK * 2 ] ;
144+ for chunk in self . inner . as_slice ( ) . chunks ( CHUNK ) {
145+ let buf_chunk = & mut buf[ ..chunk. len ( ) * 2 ] ;
146+ let chunk = encode_to_slice_inner ( chunk, buf_chunk, self . table ) . unwrap ( ) ;
147+ f. write_str ( chunk) ?;
127148 }
128- length
149+ Ok ( ( ) )
150+ }
151+ }
152+ impl fmt:: Debug for EncodeHex < ' _ > {
153+ #[ inline]
154+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
155+ fmt:: Display :: fmt ( self , f)
129156 }
130157}
131158
132159#[ inline]
133160fn encode_to_iter < T : iter:: FromIterator < char > > ( table : & ' static [ u8 ; 16 ] , source : & [ u8 ] ) -> T {
134- BytesToHexChars :: new ( source, table) . collect ( )
161+ EncodeHex :: new ( source, table) . collect ( )
135162}
136163
137164impl < T : AsRef < [ u8 ] > > ToHex for T {
@@ -172,33 +199,35 @@ pub trait FromHex: Sized {
172199 fn from_hex < T : AsRef < [ u8 ] > > ( hex : T ) -> Result < Self , Self :: Error > ;
173200}
174201
175- const fn val ( c : u8 , idx : usize ) -> Result < u8 , FromHexError > {
176- match c {
177- b'A' ..=b'F' => Ok ( c - b'A' + 10 ) ,
178- b'a' ..=b'f' => Ok ( c - b'a' + 10 ) ,
179- b'0' ..=b'9' => Ok ( c - b'0' ) ,
180- _ => Err ( FromHexError :: InvalidHexCharacter {
181- c : c as char ,
182- index : idx,
183- } ) ,
184- }
202+ #[ inline]
203+ fn val ( c : u8 , index : usize ) -> Result < u8 , FromHexError > {
204+ let c = c as char ;
205+ c. to_digit ( 16 )
206+ . map ( |x| x as u8 )
207+ . ok_or ( FromHexError :: InvalidHexCharacter { c, index } )
185208}
186209
187210#[ cfg( feature = "alloc" ) ]
188211impl FromHex for Vec < u8 > {
189212 type Error = FromHexError ;
190213
191214 fn from_hex < T : AsRef < [ u8 ] > > ( hex : T ) -> Result < Self , Self :: Error > {
192- let hex = hex. as_ref ( ) ;
193- if hex. len ( ) % 2 != 0 {
194- return Err ( FromHexError :: OddLength ) ;
195- }
215+ decode_iter ( hex. as_ref ( ) ) ?. collect ( )
216+ }
217+ }
196218
197- hex. chunks ( 2 )
198- . enumerate ( )
199- . map ( |( i, pair) | Ok ( val ( pair[ 0 ] , 2 * i) ? << 4 | val ( pair[ 1 ] , 2 * i + 1 ) ?) )
200- . collect ( )
219+ #[ inline]
220+ fn decode_iter (
221+ hex : & [ u8 ] ,
222+ ) -> Result < impl Iterator < Item = Result < u8 , FromHexError > > + ' _ , FromHexError > {
223+ let it = hex. chunks_exact ( 2 ) ;
224+ if !it. remainder ( ) . is_empty ( ) {
225+ return Err ( FromHexError :: OddLength ) ;
201226 }
227+
228+ Ok ( it
229+ . enumerate ( )
230+ . map ( |( i, pair) | Ok ( val ( pair[ 0 ] , i * 2 ) ? << 4 | val ( pair[ 1 ] , i * 2 + 1 ) ?) ) )
202231}
203232
204233// Helper macro to implement the trait for a few fixed sized arrays. Once Rust
@@ -309,32 +338,19 @@ pub fn decode<T: AsRef<[u8]>>(data: T) -> Result<Vec<u8>, FromHexError> {
309338/// assert_eq!(hex::decode_to_slice("6b697769", &mut bytes as &mut [u8]), Ok(()));
310339/// assert_eq!(&bytes, b"kiwi");
311340/// ```
341+ #[ inline]
312342pub fn decode_to_slice < T : AsRef < [ u8 ] > > ( data : T , out : & mut [ u8 ] ) -> Result < ( ) , FromHexError > {
313343 let data = data. as_ref ( ) ;
314344
315- if data. len ( ) % 2 != 0 {
316- return Err ( FromHexError :: OddLength ) ;
317- }
345+ let it = decode_iter ( data) ?;
318346 if data. len ( ) / 2 != out. len ( ) {
319347 return Err ( FromHexError :: InvalidStringLength ) ;
320348 }
321349
322- for ( i, byte) in out. iter_mut ( ) . enumerate ( ) {
323- * byte = val ( data[ 2 * i] , 2 * i) ? << 4 | val ( data[ 2 * i + 1 ] , 2 * i + 1 ) ?;
324- }
325-
326- Ok ( ( ) )
327- }
328-
329- // generates an iterator like this
330- // (0, 1)
331- // (2, 3)
332- // (4, 5)
333- // (6, 7)
334- // ...
335- #[ inline]
336- fn generate_iter ( len : usize ) -> impl Iterator < Item = ( usize , usize ) > {
337- ( 0 ..len) . step_by ( 2 ) . zip ( ( 0 ..len) . skip ( 1 ) . step_by ( 2 ) )
350+ out. iter_mut ( ) . zip ( it) . try_for_each ( |( out, byte) | {
351+ * out = byte?;
352+ Ok ( ( ) )
353+ } )
338354}
339355
340356// the inverse of `val`.
@@ -347,10 +363,10 @@ const fn byte2hex(byte: u8, table: &[u8; 16]) -> (u8, u8) {
347363 ( high, low)
348364}
349365
350- /// Encodes some bytes into a mutable slice of bytes .
366+ /// Encodes some bytes into the provided byte buffer, and then returns the buffer as a string .
351367///
352- /// The output buffer, has to be able to hold exactly `input.len() * 2` bytes,
353- /// otherwise this function will return an error.
368+ /// The output buffer has to be able to hold exactly `input.len() * 2` bytes,
369+ /// or this function will return an error.
354370///
355371/// # Example
356372///
@@ -359,8 +375,8 @@ const fn byte2hex(byte: u8, table: &[u8; 16]) -> (u8, u8) {
359375/// # fn main() -> Result<(), FromHexError> {
360376/// let mut bytes = [0u8; 4 * 2];
361377///
362- /// hex::encode_to_slice(b"kiwi", &mut bytes)?;
363- /// assert_eq!(&bytes, b "6b697769");
378+ /// let string = hex::encode_to_slice(b"kiwi", &mut bytes)?;
379+ /// assert_eq!(string, "6b697769");
364380/// # Ok(())
365381/// # }
366382/// ```
@@ -375,46 +391,123 @@ const fn byte2hex(byte: u8, table: &[u8; 16]) -> (u8, u8) {
375391/// assert_eq!(hex::encode_to_slice(b"kiwi", &mut bytes), Err(FromHexError::InvalidStringLength));
376392///
377393/// // you can do this instead:
378- /// hex::encode_to_slice(b"kiwi", &mut bytes[..4 * 2])?;
394+ /// let string = hex::encode_to_slice(b"kiwi", &mut bytes[..4 * 2])?;
395+ /// assert_eq!(string, "6b697769");
379396/// assert_eq!(&bytes, b"6b697769\0\0");
380397/// # Ok(())
381398/// # }
382399/// ```
383- pub fn encode_to_slice < T : AsRef < [ u8 ] > > ( input : T , output : & mut [ u8 ] ) -> Result < ( ) , FromHexError > {
384- if input. as_ref ( ) . len ( ) * 2 != output. len ( ) {
400+ #[ inline]
401+ pub fn encode_to_slice < T : AsRef < [ u8 ] > > (
402+ input : T ,
403+ output : & mut [ u8 ] ,
404+ ) -> Result < & mut str , FromHexError > {
405+ encode_to_slice_inner ( input. as_ref ( ) , output, HEX_CHARS_LOWER )
406+ }
407+
408+ /// Encodes some bytes into the provided byte buffer, and then returns the buffer as a string.
409+ ///
410+ /// Apart from the characters' casing, this works exactly like [`encode_to_slice()`].
411+ ///
412+ /// The output buffer has to be able to hold exactly `input.len() * 2` bytes,
413+ /// or this function will return an error.
414+ ///
415+ /// # Example
416+ ///
417+ /// ```
418+ /// # use hex::FromHexError;
419+ /// # fn main() -> Result<(), FromHexError> {
420+ /// let mut bytes = [0u8; 4 * 2];
421+ ///
422+ /// let string = hex::encode_to_slice_upper(b"kiwi", &mut bytes)?;
423+ /// assert_eq!(string, "6B697769");
424+ /// # Ok(())
425+ /// # }
426+ /// ```
427+ ///
428+ /// If the buffer is too large, an error is returned:
429+ ///
430+ /// ```
431+ /// use hex::FromHexError;
432+ /// # fn main() -> Result<(), FromHexError> {
433+ /// let mut bytes = [0_u8; 5 * 2];
434+ ///
435+ /// assert_eq!(hex::encode_to_slice_upper(b"kiwi", &mut bytes), Err(FromHexError::InvalidStringLength));
436+ ///
437+ /// // you can do this instead:
438+ /// let string = hex::encode_to_slice_upper(b"kiwi", &mut bytes[..4 * 2])?;
439+ /// assert_eq!(string, "6B697769");
440+ /// assert_eq!(&bytes, b"6B697769\0\0");
441+ /// # Ok(())
442+ /// # }
443+ /// ```
444+ #[ inline]
445+ pub fn encode_to_slice_upper < T : AsRef < [ u8 ] > > (
446+ input : T ,
447+ output : & mut [ u8 ] ,
448+ ) -> Result < & mut str , FromHexError > {
449+ encode_to_slice_inner ( input. as_ref ( ) , output, HEX_CHARS_UPPER )
450+ }
451+
452+ #[ inline]
453+ fn encode_to_slice_inner < ' a > (
454+ input : & [ u8 ] ,
455+ output : & ' a mut [ u8 ] ,
456+ table : & [ u8 ; 16 ] ,
457+ ) -> Result < & ' a mut str , FromHexError > {
458+ if input. len ( ) * 2 != output. len ( ) {
385459 return Err ( FromHexError :: InvalidStringLength ) ;
386460 }
387461
388- for ( byte, ( i, j) ) in input
389- . as_ref ( )
390- . iter ( )
391- . zip ( generate_iter ( input. as_ref ( ) . len ( ) * 2 ) )
392- {
393- let ( high, low) = byte2hex ( * byte, HEX_CHARS_LOWER ) ;
394- output[ i] = high;
395- output[ j] = low;
462+ for ( out, byte) in output. chunks_exact_mut ( 2 ) . zip ( input) {
463+ let ( high, low) = byte2hex ( * byte, table) ;
464+ out[ 0 ] = high;
465+ out[ 1 ] = low;
396466 }
397467
398- Ok ( ( ) )
468+ // SAFETY: output was just fully filled with only ascii characters
469+ let output = unsafe { core:: str:: from_utf8_unchecked_mut ( output) } ;
470+
471+ Ok ( output)
472+ }
473+
474+ /// Returns a [`Display`][fmt::Display] type that formats to the hex representation of the input.
475+ ///
476+ /// # Example
477+ ///
478+ /// ```
479+ /// let hex = hex::encode_fmt(b"\r\n");
480+ /// let s = format!("the data is: {}", hex);
481+ /// assert_eq!(s, "the data is: 0d0a");
482+ /// ```
483+ #[ inline]
484+ pub fn encode_fmt < T : AsRef < [ u8 ] > > ( input : & T ) -> EncodeHex < ' _ > {
485+ EncodeHex :: new ( input. as_ref ( ) , HEX_CHARS_LOWER )
486+ }
487+
488+ /// Returns a [`Display`][fmt::Display] type that formats to the hex representation of the input.
489+ ///
490+ /// Apart from the characters' casing, this works exactly like [`encode_fmt()`].
491+ ///
492+ /// # Example
493+ ///
494+ /// ```
495+ /// let hex = hex::encode_fmt_upper(b"\r\n");
496+ /// let s = format!("the data is: {}", hex);
497+ /// assert_eq!(s, "the data is: 0D0A");
498+ /// ```
499+ #[ inline]
500+ pub fn encode_fmt_upper < T : AsRef < [ u8 ] > > ( input : & T ) -> EncodeHex < ' _ > {
501+ EncodeHex :: new ( input. as_ref ( ) , HEX_CHARS_UPPER )
399502}
400503
401504#[ cfg( test) ]
402505mod test {
403506 use super :: * ;
404507 #[ cfg( feature = "alloc" ) ]
405508 use alloc:: string:: ToString ;
406- #[ cfg( feature = "alloc" ) ]
407- use alloc:: vec;
408509 use pretty_assertions:: assert_eq;
409510
410- #[ test]
411- #[ cfg( feature = "alloc" ) ]
412- fn test_gen_iter ( ) {
413- let result = vec ! [ ( 0 , 1 ) , ( 2 , 3 ) ] ;
414-
415- assert_eq ! ( generate_iter( 5 ) . collect:: <Vec <_>>( ) , result) ;
416- }
417-
418511 #[ test]
419512 fn test_encode_to_slice ( ) {
420513 let mut output_1 = [ 0 ; 4 * 2 ] ;
0 commit comments