@@ -18,12 +18,36 @@ use typenum::*;
18
18
19
19
use crate :: { ArrayLength , GenericArray } ;
20
20
21
- static LOWER_CHARS : [ u8 ; 16 ] = * b"0123456789abcdef" ;
22
- static UPPER_CHARS : [ u8 ; 16 ] = * b"0123456789ABCDEF" ;
21
+ #[ inline( always) ]
22
+ fn hex_encode_fallback < const UPPER : bool > ( src : & [ u8 ] , dst : & mut [ u8 ] ) {
23
+ let alphabet = match UPPER {
24
+ true => b"0123456789ABCDEF" ,
25
+ false => b"0123456789abcdef" ,
26
+ } ;
27
+
28
+ dst. chunks_exact_mut ( 2 ) . zip ( src) . for_each ( |( s, c) | {
29
+ s[ 0 ] = alphabet[ ( c >> 4 ) as usize ] ;
30
+ s[ 1 ] = alphabet[ ( c & 0xF ) as usize ] ;
31
+ } ) ;
32
+ }
33
+
34
+ #[ cfg( feature = "faster-hex" ) ]
35
+ fn faster_hex_encode < const UPPER : bool > ( src : & [ u8 ] , dst : & mut [ u8 ] ) {
36
+ debug_assert ! ( dst. len( ) >= ( src. len( ) * 2 ) ) ;
37
+
38
+ #[ cfg( miri) ]
39
+ hex_encode_fallback :: < UPPER > ( src, dst) ;
40
+
41
+ // the `unwrap_unchecked` is to avoid the length checks
42
+ #[ cfg( not( miri) ) ]
43
+ match UPPER {
44
+ true => unsafe { faster_hex:: hex_encode_upper ( src, dst) . unwrap_unchecked ( ) } ,
45
+ false => unsafe { faster_hex:: hex_encode ( src, dst) . unwrap_unchecked ( ) } ,
46
+ } ;
47
+ }
23
48
24
- fn generic_hex < N : ArrayLength > (
49
+ fn generic_hex < N : ArrayLength , const UPPER : bool > (
25
50
arr : & GenericArray < u8 , N > ,
26
- alphabet : & [ u8 ; 16 ] , // use fixed-length array to avoid slice index checks
27
51
f : & mut fmt:: Formatter < ' _ > ,
28
52
) -> fmt:: Result
29
53
where
@@ -36,32 +60,50 @@ where
36
60
_ => max_digits,
37
61
} ;
38
62
39
- let max_hex = ( max_digits >> 1 ) + ( max_digits & 1 ) ;
63
+ // ceil(max_digits / 2)
64
+ let max_bytes = ( max_digits >> 1 ) + ( max_digits & 1 ) ;
65
+
66
+ let input = {
67
+ // LLVM can't seem to automatically prove this
68
+ if max_bytes > N :: USIZE {
69
+ unsafe { core:: hint:: unreachable_unchecked ( ) } ;
70
+ }
71
+
72
+ & arr[ ..max_bytes]
73
+ } ;
40
74
41
75
if N :: USIZE <= 1024 {
42
- // For small arrays use a stack allocated
43
- // buffer of 2x number of bytes
44
- let mut res = GenericArray :: < u8 , Sum < N , N > > :: default ( ) ;
76
+ // For small arrays use a stack allocated buffer of 2x number of bytes
77
+ let mut buf = GenericArray :: < u8 , Sum < N , N > > :: default ( ) ;
45
78
46
- arr. iter ( ) . take ( max_hex) . enumerate ( ) . for_each ( |( i, c) | {
47
- res[ i * 2 ] = alphabet[ ( c >> 4 ) as usize ] ;
48
- res[ i * 2 + 1 ] = alphabet[ ( c & 0xF ) as usize ] ;
49
- } ) ;
79
+ if N :: USIZE < 16 {
80
+ // for the smallest inputs, don't bother limiting to max_bytes,
81
+ // just process the entire array. When "faster-hex" is enabled,
82
+ // this avoids its logic that winds up going to the fallback anyway
83
+ hex_encode_fallback :: < UPPER > ( arr, & mut buf) ;
84
+ } else if cfg ! ( not( feature = "faster-hex" ) ) {
85
+ hex_encode_fallback :: < UPPER > ( input, & mut buf) ;
86
+ } else {
87
+ #[ cfg( feature = "faster-hex" ) ]
88
+ faster_hex_encode :: < UPPER > ( input, & mut buf) ;
89
+ }
50
90
51
- f. write_str ( unsafe { str:: from_utf8_unchecked ( & res [ .. max_digits] ) } ) ?;
91
+ f. write_str ( unsafe { str:: from_utf8_unchecked ( buf . get_unchecked ( .. max_digits) ) } ) ?;
52
92
} else {
53
93
// For large array use chunks of up to 1024 bytes (2048 hex chars)
54
94
let mut buf = [ 0u8 ; 2048 ] ;
55
95
let mut digits_left = max_digits;
56
96
57
- for chunk in arr[ ..max_hex] . chunks ( 1024 ) {
58
- chunk. iter ( ) . enumerate ( ) . for_each ( |( i, c) | {
59
- buf[ i * 2 ] = alphabet[ ( c >> 4 ) as usize ] ;
60
- buf[ i * 2 + 1 ] = alphabet[ ( c & 0xF ) as usize ] ;
61
- } ) ;
97
+ for chunk in input. chunks ( 1024 ) {
98
+ #[ cfg( feature = "faster-hex" ) ]
99
+ faster_hex_encode :: < UPPER > ( chunk, & mut buf) ;
100
+
101
+ #[ cfg( not( feature = "faster-hex" ) ) ]
102
+ hex_encode_fallback :: < UPPER > ( chunk, buf) ;
62
103
63
104
let n = min ( chunk. len ( ) * 2 , digits_left) ;
64
- f. write_str ( unsafe { str:: from_utf8_unchecked ( & buf[ ..n] ) } ) ?;
105
+ // SAFETY: n will always be within bounds due to the above min
106
+ f. write_str ( unsafe { str:: from_utf8_unchecked ( buf. get_unchecked ( ..n) ) } ) ?;
65
107
digits_left -= n;
66
108
}
67
109
}
73
115
N : Add < N > ,
74
116
Sum < N , N > : ArrayLength ,
75
117
{
118
+ #[ inline]
76
119
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
77
- generic_hex ( self , & LOWER_CHARS , f)
120
+ generic_hex :: < _ , false > ( self , f)
78
121
}
79
122
}
80
123
83
126
N : Add < N > ,
84
127
Sum < N , N > : ArrayLength ,
85
128
{
129
+ #[ inline]
86
130
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
87
- generic_hex ( self , & UPPER_CHARS , f)
131
+ generic_hex :: < _ , true > ( self , f)
88
132
}
89
133
}
0 commit comments