1- use crate :: engine:: { general_purpose :: STANDARD , DecodeEstimate , Engine } ;
1+ use crate :: engine:: Engine ;
22#[ cfg( any( feature = "alloc" , feature = "std" , test) ) ]
33use alloc:: vec:: Vec ;
44use core:: fmt;
@@ -89,7 +89,7 @@ impl From<DecodeError> for DecodeSliceError {
8989#[ deprecated( since = "0.21.0" , note = "Use Engine::decode" ) ]
9090#[ cfg( any( feature = "alloc" , feature = "std" , test) ) ]
9191pub fn decode < T : AsRef < [ u8 ] > > ( input : T ) -> Result < Vec < u8 > , DecodeError > {
92- STANDARD . decode ( input)
92+ crate :: engine :: general_purpose :: STANDARD . decode ( input)
9393}
9494
9595/// Decode from string reference as octets using the specified [Engine].
@@ -130,6 +130,73 @@ pub fn decode_engine_slice<E: Engine, T: AsRef<[u8]>>(
130130 engine. decode_slice ( input, output)
131131}
132132
133+ /// Returns the decoded size of the `encoded` input assuming the input is valid
134+ /// base64 string.
135+ ///
136+ /// Assumes input is a valid base64-encoded string. Result is unspecified if it
137+ /// isn’t.
138+ ///
139+ /// If you don’t need a precise length of the decoded string, you can use
140+ /// [`decoded_len_estimate`] function instead. It’s faster and provides an
141+ /// estimate which is only at most two bytes off from the real length.
142+ ///
143+ /// # Examples
144+ ///
145+ /// ```
146+ /// use base64::decoded_len;
147+ ///
148+ /// assert_eq!(0, decoded_len(b""));
149+ /// assert_eq!(1, decoded_len(b"AA"));
150+ /// assert_eq!(2, decoded_len(b"AAA"));
151+ /// assert_eq!(3, decoded_len(b"AAAA"));
152+ /// assert_eq!(1, decoded_len(b"AA=="));
153+ /// assert_eq!(2, decoded_len(b"AAA="));
154+ /// ```
155+ pub fn decoded_len ( encoded : impl AsRef < [ u8 ] > ) -> usize {
156+ let encoded = encoded. as_ref ( ) ;
157+ if encoded. len ( ) < 2 {
158+ return 0 ;
159+ }
160+ let is_pad = |idx| ( encoded[ encoded. len ( ) - idx] == b'=' ) as usize ;
161+ let len = encoded. len ( ) - is_pad ( 1 ) - is_pad ( 2 ) ;
162+ match len % 4 {
163+ 0 => len / 4 * 3 ,
164+ remainder => len / 4 * 3 + remainder - 1 ,
165+ }
166+ }
167+
168+ #[ test]
169+ fn test_decoded_len ( ) {
170+ for chunks in 0 ..25 {
171+ let mut input = vec ! [ b'A' ; chunks * 4 + 4 ] ;
172+ assert_eq ! ( chunks * 3 + 0 , decoded_len( & input[ ..chunks * 4 ] ) ) ;
173+ assert_eq ! ( chunks * 3 + 1 , decoded_len( & input[ ..chunks * 4 + 2 ] ) ) ;
174+ assert_eq ! ( chunks * 3 + 2 , decoded_len( & input[ ..chunks * 4 + 3 ] ) ) ;
175+ assert_eq ! ( chunks * 3 + 3 , decoded_len( & input[ ..chunks * 4 + 4 ] ) ) ;
176+
177+ input[ chunks * 4 + 3 ] = b'=' ;
178+ assert_eq ! ( chunks * 3 + 1 , decoded_len( & input[ ..chunks * 4 + 2 ] ) ) ;
179+ assert_eq ! ( chunks * 3 + 2 , decoded_len( & input[ ..chunks * 4 + 3 ] ) ) ;
180+ assert_eq ! ( chunks * 3 + 2 , decoded_len( & input[ ..chunks * 4 + 4 ] ) ) ;
181+ input[ chunks * 4 + 2 ] = b'=' ;
182+ assert_eq ! ( chunks * 3 + 1 , decoded_len( & input[ ..chunks * 4 + 2 ] ) ) ;
183+ assert_eq ! ( chunks * 3 + 1 , decoded_len( & input[ ..chunks * 4 + 3 ] ) ) ;
184+ assert_eq ! ( chunks * 3 + 1 , decoded_len( & input[ ..chunks * 4 + 4 ] ) ) ;
185+ }
186+
187+ // Mustn’t panic or overflow if given bogus input.
188+ for len in 1 ..100 {
189+ let mut input = vec ! [ b'A' ; len] ;
190+ let got = decoded_len ( & input) ;
191+ debug_assert ! ( got <= len) ;
192+ for padding in 1 ..=len. min ( 10 ) {
193+ input[ len - padding] = b'=' ;
194+ let got = decoded_len ( & input) ;
195+ debug_assert ! ( got <= len) ;
196+ }
197+ }
198+ }
199+
133200/// Returns a conservative estimate of the decoded size of `encoded_len` base64 symbols (rounded up
134201/// to the next group of 3 decoded bytes).
135202///
@@ -141,6 +208,7 @@ pub fn decode_engine_slice<E: Engine, T: AsRef<[u8]>>(
141208/// ```
142209/// use base64::decoded_len_estimate;
143210///
211+ /// assert_eq!(0, decoded_len_estimate(0));
144212/// assert_eq!(3, decoded_len_estimate(1));
145213/// assert_eq!(3, decoded_len_estimate(2));
146214/// assert_eq!(3, decoded_len_estimate(3));
@@ -149,17 +217,27 @@ pub fn decode_engine_slice<E: Engine, T: AsRef<[u8]>>(
149217/// assert_eq!(6, decoded_len_estimate(5));
150218/// ```
151219pub fn decoded_len_estimate ( encoded_len : usize ) -> usize {
152- STANDARD
153- . internal_decoded_len_estimate ( encoded_len)
154- . decoded_len_estimate ( )
220+ ( encoded_len / 4 + ( encoded_len % 4 > 0 ) as usize ) * 3
221+ }
222+
223+ #[ test]
224+ fn test_decode_len_estimate ( ) {
225+ for chunks in 0 ..250 {
226+ assert_eq ! ( chunks * 3 , decoded_len_estimate( chunks * 4 ) ) ;
227+ assert_eq ! ( chunks * 3 + 3 , decoded_len_estimate( chunks * 4 + 1 ) ) ;
228+ assert_eq ! ( chunks * 3 + 3 , decoded_len_estimate( chunks * 4 + 2 ) ) ;
229+ assert_eq ! ( chunks * 3 + 3 , decoded_len_estimate( chunks * 4 + 3 ) ) ;
230+ }
231+ // Mustn’t panic or overflow.
232+ assert_eq ! ( usize :: MAX / 4 * 3 + 3 , decoded_len_estimate( usize :: MAX ) ) ;
155233}
156234
157235#[ cfg( test) ]
158236mod tests {
159237 use super :: * ;
160238 use crate :: {
161- alphabet ,
162- engine:: { general_purpose, Config , GeneralPurpose } ,
239+ engine :: { Config , GeneralPurpose } ,
240+ engine:: general_purpose:: { STANDARD , NO_PAD } ,
163241 tests:: { assert_encode_sanity, random_engine} ,
164242 } ;
165243 use rand:: {
@@ -245,7 +323,7 @@ mod tests {
245323
246324 #[ test]
247325 fn decode_engine_estimation_works_for_various_lengths ( ) {
248- let engine = GeneralPurpose :: new ( & alphabet:: STANDARD , general_purpose :: NO_PAD ) ;
326+ let engine = GeneralPurpose :: new ( & crate :: alphabet:: STANDARD , NO_PAD ) ;
249327 for num_prefix_quads in 0 ..100 {
250328 for suffix in & [ "AA" , "AAA" , "AAAA" ] {
251329 let mut prefix = "AAAA" . repeat ( num_prefix_quads) ;
0 commit comments