@@ -20,9 +20,9 @@ use crate::alloc::{string::String, vec::Vec};
2020#[ cfg( feature = "alloc" ) ]
2121use crate :: alloc:: format;
2222
23- #[ cfg( feature = "std" ) ]
23+ #[ cfg( any ( test , feature = "std" ) ) ]
2424use std:: io;
25- #[ cfg( all( not( feature = "std" ) , feature = "core2" ) ) ]
25+ #[ cfg( all( not( test ) , not ( feature = "std" ) , feature = "core2" ) ) ]
2626use core2:: io;
2727
2828use core:: { fmt, str} ;
@@ -219,6 +219,51 @@ impl ToHex for [u8] {
219219 }
220220}
221221
222+ /// A struct implementing [`io::Write`] that converts what's written to it into
223+ /// a hex String.
224+ ///
225+ /// If you already have the data to be converted in a `Vec<u8>` use [`ToHex`]
226+ /// but if you have an encodable object, by using this you avoid the
227+ /// serialization to `Vec<u8>` by going directly to `String`.
228+ ///
229+ /// Note that to achieve better perfomance than [`ToHex`] the struct must be
230+ /// created with the right `capacity` of the final hex string so that the inner
231+ /// `String` doesn't re-allocate.
232+ #[ cfg( any( test, feature = "std" , feature = "alloc" ) ) ]
233+ #[ cfg_attr( docsrs, doc( cfg( any( test, feature = "std" , feature = "alloc" ) ) ) ) ]
234+ pub struct HexWriter ( String ) ;
235+
236+ #[ cfg( any( test, feature = "std" , feature = "alloc" ) ) ]
237+ #[ cfg_attr( docsrs, doc( cfg( any( test, feature = "std" , feature = "alloc" ) ) ) ) ]
238+ impl HexWriter {
239+ /// Creates a new [`HexWriter`] with the `capacity` of the inner `String`
240+ /// that will contain final hex value.
241+ pub fn new ( capacity : usize ) -> Self {
242+ HexWriter ( String :: with_capacity ( capacity) )
243+ }
244+
245+ /// Returns the resulting hex string.
246+ pub fn result ( self ) -> String {
247+ self . 0
248+ }
249+ }
250+
251+ #[ cfg( any( test, feature = "std" , feature = "alloc" ) ) ]
252+ #[ cfg_attr( docsrs, doc( cfg( any( test, feature = "std" , feature = "alloc" ) ) ) ) ]
253+ impl io:: Write for HexWriter {
254+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
255+ use core:: fmt:: Write ;
256+ for ch in buf {
257+ write ! ( self . 0 , "{:02x}" , ch) . expect ( "writing to string" ) ;
258+ }
259+ Ok ( buf. len ( ) )
260+ }
261+
262+ fn flush ( & mut self ) -> io:: Result < ( ) > {
263+ Ok ( ( ) )
264+ }
265+ }
266+
222267#[ cfg( any( test, feature = "std" , feature = "alloc" ) ) ]
223268#[ cfg_attr( docsrs, doc( cfg( any( feature = "std" , feature = "alloc" ) ) ) ) ]
224269impl FromHex for Vec < u8 > {
@@ -276,6 +321,7 @@ mod tests {
276321 use super :: * ;
277322
278323 use core:: fmt;
324+ use std:: io:: Write ;
279325
280326 #[ test]
281327 #[ cfg( any( feature = "std" , feature = "alloc" ) ) ]
@@ -398,5 +444,41 @@ mod tests {
398444 Err ( Error :: InvalidChar ( 194 ) )
399445 ) ;
400446 }
447+
448+
449+ #[ test]
450+ fn hex_writer ( ) {
451+ let vec: Vec < _ > = ( 0u8 ..32 ) . collect ( ) ;
452+ let mut writer = HexWriter :: new ( 64 ) ;
453+ writer. write_all ( & vec[ ..] ) . unwrap ( ) ;
454+ assert_eq ! ( vec. to_hex( ) , writer. result( ) ) ;
455+ }
401456}
402457
458+
459+ #[ cfg( all( test, feature="unstable" ) ) ]
460+ mod benches {
461+ use test:: { Bencher , black_box} ;
462+ use super :: { ToHex , HexWriter } ;
463+ use std:: io:: Write ;
464+ use crate :: { sha256, Hash } ;
465+
466+ #[ bench]
467+ fn bench_to_hex ( bh : & mut Bencher ) {
468+ let hash = sha256:: Hash :: hash ( & [ 0 ; 1 ] ) ;
469+ bh. iter ( || {
470+ black_box ( hash. to_hex ( ) ) ;
471+ } )
472+ }
473+
474+
475+ #[ bench]
476+ fn bench_to_hex_writer ( bh : & mut Bencher ) {
477+ let hash = sha256:: Hash :: hash ( & [ 0 ; 1 ] ) ;
478+ bh. iter ( || {
479+ let mut writer = HexWriter :: new ( 64 ) ;
480+ writer. write_all ( hash. as_inner ( ) ) . unwrap ( ) ;
481+ black_box ( writer. result ( ) ) ;
482+ } )
483+ }
484+ }
0 commit comments