@@ -1671,7 +1671,7 @@ pub mod stream {
16711671pub mod ct {
16721672 use super :: {
16731673 Alphabet , DecodeError , DecodedBuffer , Standard , UrlSafe , ct_decode_in_place,
1674- ct_decode_slice, ct_validate_decode,
1674+ ct_decode_slice, ct_decoded_len , ct_validate_decode,
16751675 } ;
16761676 use core:: marker:: PhantomData ;
16771677
@@ -1748,6 +1748,15 @@ pub mod ct {
17481748 self . validate_result ( input) . is_ok ( )
17491749 }
17501750
1751+ /// Returns the exact decoded length for valid input.
1752+ ///
1753+ /// This uses the same constant-time-oriented validation policy as
1754+ /// [`Self::decode_slice`] before returning a length. Input length,
1755+ /// padding length, and final success or failure remain public.
1756+ pub fn decoded_len ( & self , input : & [ u8 ] ) -> Result < usize , DecodeError > {
1757+ ct_decoded_len :: < A , PAD > ( input)
1758+ }
1759+
17511760 /// Decodes `input` into `output`, returning the number of bytes
17521761 /// written.
17531762 ///
@@ -6024,6 +6033,25 @@ fn ct_validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<(),
60246033 }
60256034}
60266035
6036+ fn ct_decoded_len < A : Alphabet , const PAD : bool > ( input : & [ u8 ] ) -> Result < usize , DecodeError > {
6037+ ct_validate_decode :: < A , PAD > ( input) ?;
6038+ if input. is_empty ( ) {
6039+ return Ok ( 0 ) ;
6040+ }
6041+
6042+ if PAD {
6043+ Ok ( input. len ( ) / 4 * 3 - ct_padding_len ( input) )
6044+ } else {
6045+ let full_quads = input. len ( ) / 4 * 3 ;
6046+ match input. len ( ) % 4 {
6047+ 0 => Ok ( full_quads) ,
6048+ 2 => Ok ( full_quads + 1 ) ,
6049+ 3 => Ok ( full_quads + 2 ) ,
6050+ _ => Err ( DecodeError :: InvalidLength ) ,
6051+ }
6052+ }
6053+ }
6054+
60276055fn ct_validate_padded < A : Alphabet > ( input : & [ u8 ] ) -> Result < ( ) , DecodeError > {
60286056 if !input. len ( ) . is_multiple_of ( 4 ) {
60296057 return Err ( DecodeError :: InvalidLength ) ;
0 commit comments