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