1
- use crate :: engine:: Engine ;
2
- #[ cfg( any( feature = "alloc" , feature = "std" , test) ) ]
3
- use crate :: engine:: STANDARD ;
1
+ use crate :: engine:: { DecodeEstimate , Engine , STANDARD } ;
4
2
#[ cfg( any( feature = "alloc" , feature = "std" , test) ) ]
5
3
use alloc:: vec:: Vec ;
6
4
use core:: fmt;
@@ -44,17 +42,44 @@ impl fmt::Display for DecodeError {
44
42
45
43
#[ cfg( any( feature = "std" , test) ) ]
46
44
impl error:: Error for DecodeError {
47
- fn description ( & self ) -> & str {
48
- match * self {
49
- Self :: InvalidByte ( _, _) => "invalid byte" ,
50
- Self :: InvalidLength => "invalid length" ,
51
- Self :: InvalidLastSymbol ( _, _) => "invalid last symbol" ,
52
- Self :: InvalidPadding => "invalid padding" ,
45
+ fn cause ( & self ) -> Option < & dyn error:: Error > {
46
+ None
47
+ }
48
+ }
49
+
50
+ /// Errors that can occur while decoding into a slice.
51
+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
52
+ pub enum DecodeSliceError {
53
+ /// A [DecodeError] occurred
54
+ DecodeError ( DecodeError ) ,
55
+ /// The provided slice _may_ be too small.
56
+ ///
57
+ /// The check is conservative (assumes the last triplet of output bytes will all be needed).
58
+ OutputSliceTooSmall ,
59
+ }
60
+
61
+ impl fmt:: Display for DecodeSliceError {
62
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
63
+ match self {
64
+ Self :: DecodeError ( e) => write ! ( f, "DecodeError: {}" , e) ,
65
+ Self :: OutputSliceTooSmall => write ! ( f, "Output slice too small" ) ,
53
66
}
54
67
}
68
+ }
55
69
70
+ #[ cfg( any( feature = "std" , test) ) ]
71
+ impl error:: Error for DecodeSliceError {
56
72
fn cause ( & self ) -> Option < & dyn error:: Error > {
57
- None
73
+ match self {
74
+ DecodeSliceError :: DecodeError ( e) => Some ( e) ,
75
+ DecodeSliceError :: OutputSliceTooSmall => None ,
76
+ }
77
+ }
78
+ }
79
+
80
+ impl From < DecodeError > for DecodeSliceError {
81
+ fn from ( e : DecodeError ) -> Self {
82
+ DecodeSliceError :: DecodeError ( e)
58
83
}
59
84
}
60
85
@@ -101,10 +126,39 @@ pub fn decode_engine_slice<E: Engine, T: AsRef<[u8]>>(
101
126
input : T ,
102
127
output : & mut [ u8 ] ,
103
128
engine : & E ,
104
- ) -> Result < usize , DecodeError > {
129
+ ) -> Result < usize , DecodeSliceError > {
105
130
engine. decode_slice ( input, output)
106
131
}
107
132
133
+ /// Returns a conservative estimate of the decoded size of `encoded_len` base64 symbols (rounded up
134
+ /// to the next group of 3 decoded bytes).
135
+ ///
136
+ /// The resulting length will be a safe choice for the size of a decode buffer, but may have up to
137
+ /// 2 trailing bytes that won't end up being needed.
138
+ ///
139
+ /// # Examples
140
+ ///
141
+ /// ```
142
+ /// use base64::decoded_len_estimate;
143
+ ///
144
+ /// assert_eq!(3, decoded_len_estimate(1));
145
+ /// assert_eq!(3, decoded_len_estimate(2));
146
+ /// assert_eq!(3, decoded_len_estimate(3));
147
+ /// assert_eq!(3, decoded_len_estimate(4));
148
+ /// // start of the next quad of encoded symbols
149
+ /// assert_eq!(6, decoded_len_estimate(5));
150
+ /// ```
151
+ ///
152
+ /// # Panics
153
+ ///
154
+ /// Panics if decoded length estimation overflows.
155
+ /// This would happen for sizes within a few bytes of the maximum value of `usize`.
156
+ pub fn decoded_len_estimate ( encoded_len : usize ) -> usize {
157
+ STANDARD
158
+ . internal_decoded_len_estimate ( encoded_len)
159
+ . decoded_len_estimate ( )
160
+ }
161
+
108
162
#[ cfg( test) ]
109
163
mod tests {
110
164
use super :: * ;
@@ -235,42 +289,6 @@ mod tests {
235
289
}
236
290
}
237
291
238
- #[ test]
239
- fn decode_into_slice_fits_in_precisely_sized_slice ( ) {
240
- let mut orig_data = Vec :: new ( ) ;
241
- let mut encoded_data = String :: new ( ) ;
242
- let mut decode_buf = Vec :: new ( ) ;
243
-
244
- let input_len_range = Uniform :: new ( 0 , 1000 ) ;
245
- let mut rng = rand:: rngs:: SmallRng :: from_entropy ( ) ;
246
-
247
- for _ in 0 ..10_000 {
248
- orig_data. clear ( ) ;
249
- encoded_data. clear ( ) ;
250
- decode_buf. clear ( ) ;
251
-
252
- let input_len = input_len_range. sample ( & mut rng) ;
253
-
254
- for _ in 0 ..input_len {
255
- orig_data. push ( rng. gen ( ) ) ;
256
- }
257
-
258
- let engine = random_engine ( & mut rng) ;
259
- engine. encode_string ( & orig_data, & mut encoded_data) ;
260
- assert_encode_sanity ( & encoded_data, engine. config ( ) . encode_padding ( ) , input_len) ;
261
-
262
- decode_buf. resize ( input_len, 0 ) ;
263
-
264
- // decode into the non-empty buf
265
- let decode_bytes_written = engine
266
- . decode_slice ( & encoded_data, & mut decode_buf[ ..] )
267
- . unwrap ( ) ;
268
-
269
- assert_eq ! ( orig_data. len( ) , decode_bytes_written) ;
270
- assert_eq ! ( orig_data, decode_buf) ;
271
- }
272
- }
273
-
274
292
#[ test]
275
293
fn decode_engine_estimation_works_for_various_lengths ( ) {
276
294
let engine = GeneralPurpose :: new ( & alphabet:: STANDARD , general_purpose:: NO_PAD ) ;
@@ -284,4 +302,32 @@ mod tests {
284
302
}
285
303
}
286
304
}
305
+
306
+ #[ test]
307
+ fn decode_slice_output_length_errors ( ) {
308
+ for num_quads in 1 ..100 {
309
+ let input = "AAAA" . repeat ( num_quads) ;
310
+ let mut vec = vec ! [ 0 ; ( num_quads - 1 ) * 3 ] ;
311
+ assert_eq ! (
312
+ DecodeSliceError :: OutputSliceTooSmall ,
313
+ STANDARD . decode_slice( & input, & mut vec) . unwrap_err( )
314
+ ) ;
315
+ vec. push ( 0 ) ;
316
+ assert_eq ! (
317
+ DecodeSliceError :: OutputSliceTooSmall ,
318
+ STANDARD . decode_slice( & input, & mut vec) . unwrap_err( )
319
+ ) ;
320
+ vec. push ( 0 ) ;
321
+ assert_eq ! (
322
+ DecodeSliceError :: OutputSliceTooSmall ,
323
+ STANDARD . decode_slice( & input, & mut vec) . unwrap_err( )
324
+ ) ;
325
+ vec. push ( 0 ) ;
326
+ // now it works
327
+ assert_eq ! (
328
+ num_quads * 3 ,
329
+ STANDARD . decode_slice( & input, & mut vec) . unwrap( )
330
+ ) ;
331
+ }
332
+ }
287
333
}
0 commit comments