@@ -22,6 +22,26 @@ pub struct Base64SimdWrapper {
2222}
2323
2424impl Base64SimdWrapper {
25+ fn decode_with_standard ( input : & [ u8 ] , output : & mut Vec < u8 > ) -> Result < ( ) , ( ) > {
26+ match base64_simd:: STANDARD . decode_to_vec ( input) {
27+ Ok ( decoded_bytes) => {
28+ output. extend_from_slice ( & decoded_bytes) ;
29+ Ok ( ( ) )
30+ }
31+ Err ( _) => Err ( ( ) ) ,
32+ }
33+ }
34+
35+ fn decode_with_no_pad ( input : & [ u8 ] , output : & mut Vec < u8 > ) -> Result < ( ) , ( ) > {
36+ match base64_simd:: STANDARD_NO_PAD . decode_to_vec ( input) {
37+ Ok ( decoded_bytes) => {
38+ output. extend_from_slice ( & decoded_bytes) ;
39+ Ok ( ( ) )
40+ }
41+ Err ( _) => Err ( ( ) ) ,
42+ }
43+ }
44+
2545 pub fn new (
2646 use_padding : bool ,
2747 valid_decoding_multiple : usize ,
@@ -47,22 +67,64 @@ impl SupportsFastDecodeAndEncode for Base64SimdWrapper {
4767 }
4868
4969 fn decode_into_vec ( & self , input : & [ u8 ] , output : & mut Vec < u8 > ) -> UResult < ( ) > {
50- let decoded = if self . use_padding {
51- base64_simd:: STANDARD . decode_to_vec ( input)
70+ let original_len = output. len ( ) ;
71+
72+ let decode_result = if self . use_padding {
73+ // GNU coreutils keeps decoding even when '=' appears before the true end
74+ // of the stream (e.g. concatenated padded chunks). Mirror that logic
75+ // by splitting at each '='-containing quantum, decoding those 4-byte
76+ // groups with the padded variant, then letting the remainder fall back
77+ // to whichever alphabet fits.
78+ let mut start = 0usize ;
79+ while start < input. len ( ) {
80+ let remaining = & input[ start..] ;
81+
82+ if remaining. is_empty ( ) {
83+ break ;
84+ }
85+
86+ if let Some ( eq_rel_idx) = remaining. iter ( ) . position ( |& b| b == b'=' ) {
87+ let blocks = ( eq_rel_idx / 4 ) + 1 ;
88+ let segment_len = blocks * 4 ;
89+
90+ if segment_len > remaining. len ( ) {
91+ return Err ( USimpleError :: new ( 1 , "error: invalid input" . to_owned ( ) ) ) ;
92+ }
93+
94+ if Self :: decode_with_standard ( & remaining[ ..segment_len] , output) . is_err ( ) {
95+ return Err ( USimpleError :: new ( 1 , "error: invalid input" . to_owned ( ) ) ) ;
96+ }
97+
98+ start += segment_len;
99+ } else {
100+ // If there are no more '=' bytes the tail might still be padded
101+ // (len % 4 == 0) or purposely unpadded (GNU --ignore-garbage or
102+ // concatenated streams), so select the matching alphabet.
103+ let decoder = if remaining. len ( ) % 4 == 0 {
104+ Self :: decode_with_standard
105+ } else {
106+ Self :: decode_with_no_pad
107+ } ;
108+
109+ if decoder ( remaining, output) . is_err ( ) {
110+ return Err ( USimpleError :: new ( 1 , "error: invalid input" . to_owned ( ) ) ) ;
111+ }
112+
113+ break ;
114+ }
115+ }
116+
117+ Ok ( ( ) )
52118 } else {
53- base64_simd:: STANDARD_NO_PAD . decode_to_vec ( input)
119+ Self :: decode_with_no_pad ( input, output)
120+ . map_err ( |_| USimpleError :: new ( 1 , "error: invalid input" . to_owned ( ) ) )
54121 } ;
55122
56- match decoded {
57- Ok ( decoded_bytes) => {
58- output. extend_from_slice ( & decoded_bytes) ;
59- Ok ( ( ) )
60- }
61- Err ( _) => {
62- // Restore original length on error
63- output. truncate ( output. len ( ) ) ;
64- Err ( USimpleError :: new ( 1 , "error: invalid input" . to_owned ( ) ) )
65- }
123+ if let Err ( err) = decode_result {
124+ output. truncate ( original_len) ;
125+ Err ( err)
126+ } else {
127+ Ok ( ( ) )
66128 }
67129 }
68130
0 commit comments