1
1
use std:: ops:: { BitAnd , BitOr , Shl , Shr } ;
2
2
3
- pub fn encode < T : AsRef < [ u8 ] > > ( input : T ) -> String {
4
- let encoded_size =
5
- encoded_len ( input. as_ref ( ) . len ( ) ) . expect ( "integer overflow when calculating buffer size" ) ;
6
- let mut buf = vec ! [ 0 ; encoded_size] ;
7
-
8
- encode_with_padding ( input. as_ref ( ) , & mut buf[ ..] , encoded_size) ;
9
-
10
- String :: from_utf8 ( buf) . expect ( "Invalid UTF8" )
11
- }
12
-
13
- pub fn encoded_len ( bytes_len : usize ) -> Option < usize > {
14
- let rem = bytes_len % 3 ;
15
-
16
- let complete_input_chunks = bytes_len / 3 ;
17
- let complete_chunk_output = complete_input_chunks. checked_mul ( 4 ) ;
18
-
19
- if rem > 0 {
20
- complete_chunk_output. and_then ( |c| c. checked_add ( 4 ) )
21
- } else {
22
- complete_chunk_output
23
- }
24
- }
25
-
26
- fn encode_with_padding ( input : & [ u8 ] , output : & mut [ u8 ] , expected_encoded_size : usize ) {
27
- debug_assert_eq ! ( expected_encoded_size, output. len( ) ) ;
28
-
29
- let b64_bytes_written = encode_bytes ( input, output) ;
30
-
31
- let padding_bytes = add_padding ( input. len ( ) , & mut output[ b64_bytes_written..] ) ;
32
-
33
- let encoded_bytes = b64_bytes_written
34
- . checked_add ( padding_bytes)
35
- . expect ( "usize overflow when calculating b64 length" ) ;
36
-
37
- debug_assert_eq ! ( expected_encoded_size, encoded_bytes) ;
38
- }
39
-
40
- pub fn add_padding ( input_len : usize , output : & mut [ u8 ] ) -> usize {
41
- // TODO base on encoded len to use cheaper mod by 4 (aka & 7)
42
- let rem = input_len % 3 ;
43
- let mut bytes_written = 0 ;
44
- for _ in 0 ..( ( 3 - rem) % 3 ) {
45
- output[ bytes_written] = PAD_BYTE ;
46
- bytes_written += 1 ;
47
- }
48
-
49
- bytes_written
50
- }
51
3
const PAD_BYTE : u8 = b'=' ;
52
4
const ENCODE_TABLE : & [ u8 ] =
53
5
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" . as_bytes ( ) ;
6
+ const LOW_SIX_BITS : u32 = 0x3F ;
54
7
55
- fn encode_bytes ( input : & [ u8 ] , output : & mut [ u8 ] ) -> usize {
56
- // complete chunks first
8
+ pub fn encode ( input : & [ u8 ] ) -> String {
9
+ let rem = input. len ( ) % 3 ;
10
+ let complete_chunks = input. len ( ) / 3 ;
11
+ let remainder_chunk = if rem == 0 { 0 } else { 1 } ;
12
+ let encoded_size = ( complete_chunks + remainder_chunk) * 4 ;
57
13
58
- const LOW_SIX_BITS : u32 = 0x3F ;
14
+ let mut output = vec ! [ 0 ; encoded_size ] ;
59
15
60
- let rem = input. len ( ) % 3 ;
61
- // will never underflow
16
+ // complete chunks first
62
17
let complete_chunk_len = input. len ( ) - rem;
63
18
64
19
let mut input_index = 0_usize ;
65
20
let mut output_index = 0_usize ;
66
- if let Some ( last_complete_chunk_index) = complete_chunk_len. checked_sub ( 3 ) {
67
- while input_index <= last_complete_chunk_index {
68
- let chunk = & input[ input_index..input_index + 3 ] ;
69
-
70
- // populate low 24 bits from 3 bytes
71
- let chunk_int: u32 =
72
- ( chunk[ 0 ] as u32 ) . shl ( 16 ) | ( chunk[ 1 ] as u32 ) . shl ( 8 ) | ( chunk[ 2 ] as u32 ) ;
73
- // encode 4x 6-bit output bytes
74
- output[ output_index] = ENCODE_TABLE [ chunk_int. shr ( 18 ) as usize ] ;
75
- output[ output_index + 1 ] =
76
- ENCODE_TABLE [ chunk_int. shr ( 12_u8 ) . bitand ( LOW_SIX_BITS ) as usize ] ;
77
- output[ output_index + 2 ] =
78
- ENCODE_TABLE [ chunk_int. shr ( 6_u8 ) . bitand ( LOW_SIX_BITS ) as usize ] ;
79
- output[ output_index + 3 ] = ENCODE_TABLE [ chunk_int. bitand ( LOW_SIX_BITS ) as usize ] ;
80
-
81
- input_index += 3 ;
82
- output_index += 4 ;
83
- }
21
+ while input_index < complete_chunk_len {
22
+ let chunk = & input[ input_index..input_index + 3 ] ;
23
+
24
+ // populate low 24 bits from 3 bytes
25
+ let chunk_int: u32 =
26
+ ( chunk[ 0 ] as u32 ) . shl ( 16 ) | ( chunk[ 1 ] as u32 ) . shl ( 8 ) | ( chunk[ 2 ] as u32 ) ;
27
+ // encode 4x 6-bit output bytes
28
+ output[ output_index] = ENCODE_TABLE [ chunk_int. shr ( 18 ) as usize ] ;
29
+ output[ output_index + 1 ] = ENCODE_TABLE [ chunk_int. shr ( 12_u8 ) . bitand ( LOW_SIX_BITS ) as usize ] ;
30
+ output[ output_index + 2 ] = ENCODE_TABLE [ chunk_int. shr ( 6_u8 ) . bitand ( LOW_SIX_BITS ) as usize ] ;
31
+ output[ output_index + 3 ] = ENCODE_TABLE [ chunk_int. bitand ( LOW_SIX_BITS ) as usize ] ;
32
+
33
+ input_index += 3 ;
34
+ output_index += 4 ;
84
35
}
85
36
86
37
// then leftovers
@@ -95,17 +46,16 @@ fn encode_bytes(input: &[u8], output: &mut [u8]) -> usize {
95
46
// bottom 4 bits of [1], with the 2 bottom bits as zero
96
47
output[ output_index + 2 ] =
97
48
ENCODE_TABLE [ ( chunk[ 1 ] . shl ( 2_u8 ) as u32 ) . bitand ( LOW_SIX_BITS ) as usize ] ;
98
-
99
- output_index += 3 ;
49
+ output[ output_index + 3 ] = PAD_BYTE ;
100
50
} else if rem == 1 {
101
51
let byte = input[ input_index] ;
102
52
output[ output_index] = ENCODE_TABLE [ byte. shr ( 2 ) as usize ] ;
103
53
output[ output_index + 1 ] =
104
54
ENCODE_TABLE [ ( byte. shl ( 4_u8 ) as u32 ) . bitand ( LOW_SIX_BITS ) as usize ] ;
105
- output_index += 2 ;
55
+ output[ output_index + 2 ] = PAD_BYTE ;
56
+ output[ output_index + 3 ] = PAD_BYTE ;
106
57
}
107
-
108
- output_index
58
+ String :: from_utf8 ( output) . expect ( "Invalid UTF8" )
109
59
}
110
60
111
61
#[ cfg( test) ]
0 commit comments