Skip to content

Commit 0f981bd

Browse files
Add decode_slice_unchecked to restore ability to decode into a precisely sized slice
1 parent a51e822 commit 0f981bd

File tree

3 files changed

+99
-46
lines changed

3 files changed

+99
-46
lines changed

RELEASE-NOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ precisely, see the following table.
3737
| URL_SAFE | URL_SAFE | true | Indifferent |
3838
| URL_SAFE_NO_PAD | URL_SAFE | false | Indifferent |
3939

40+
# 0.21.0-rc.1
41+
42+
- Restore the ability to decode into a slice of precisely the correct length with `Engine.decode_slice_unchecked`.
4043

4144
# 0.21.0-beta.2
4245

src/decode.rs

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,66 @@ mod tests {
235235
}
236236

237237
#[test]
238-
fn decode_into_slice_doesnt_clobber_existing_prefix_or_suffix() {
238+
fn decode_slice_doesnt_clobber_existing_prefix_or_suffix() {
239+
do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| {
240+
e.decode_slice(input, output).unwrap()
241+
})
242+
}
243+
244+
#[test]
245+
fn decode_slice_unchecked_doesnt_clobber_existing_prefix_or_suffix() {
246+
do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| {
247+
e.decode_slice_unchecked(input, output).unwrap()
248+
})
249+
}
250+
251+
#[test]
252+
fn decode_engine_estimation_works_for_various_lengths() {
253+
let engine = GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD);
254+
for num_prefix_quads in 0..100 {
255+
for suffix in &["AA", "AAA", "AAAA"] {
256+
let mut prefix = "AAAA".repeat(num_prefix_quads);
257+
prefix.push_str(suffix);
258+
// make sure no overflow (and thus a panic) occurs
259+
let res = engine.decode(prefix);
260+
assert!(res.is_ok());
261+
}
262+
}
263+
}
264+
265+
#[test]
266+
fn decode_slice_output_length_errors() {
267+
for num_quads in 1..100 {
268+
let input = "AAAA".repeat(num_quads);
269+
let mut vec = vec![0; (num_quads - 1) * 3];
270+
assert_eq!(
271+
DecodeSliceError::OutputSliceTooSmall,
272+
STANDARD.decode_slice(&input, &mut vec).unwrap_err()
273+
);
274+
vec.push(0);
275+
assert_eq!(
276+
DecodeSliceError::OutputSliceTooSmall,
277+
STANDARD.decode_slice(&input, &mut vec).unwrap_err()
278+
);
279+
vec.push(0);
280+
assert_eq!(
281+
DecodeSliceError::OutputSliceTooSmall,
282+
STANDARD.decode_slice(&input, &mut vec).unwrap_err()
283+
);
284+
vec.push(0);
285+
// now it works
286+
assert_eq!(
287+
num_quads * 3,
288+
STANDARD.decode_slice(&input, &mut vec).unwrap()
289+
);
290+
}
291+
}
292+
293+
fn do_decode_slice_doesnt_clobber_existing_prefix_or_suffix<
294+
F: Fn(&GeneralPurpose, &[u8], &mut [u8]) -> usize,
295+
>(
296+
call_decode: F,
297+
) {
239298
let mut orig_data = Vec::new();
240299
let mut encoded_data = String::new();
241300
let mut decode_buf = Vec::new();
@@ -272,9 +331,8 @@ mod tests {
272331
let offset = 1000;
273332

274333
// decode into the non-empty buf
275-
let decode_bytes_written = engine
276-
.decode_slice(&encoded_data, &mut decode_buf[offset..])
277-
.unwrap();
334+
let decode_bytes_written =
335+
call_decode(&engine, encoded_data.as_bytes(), &mut decode_buf[offset..]);
278336

279337
assert_eq!(orig_data.len(), decode_bytes_written);
280338
assert_eq!(
@@ -288,46 +346,4 @@ mod tests {
288346
);
289347
}
290348
}
291-
292-
#[test]
293-
fn decode_engine_estimation_works_for_various_lengths() {
294-
let engine = GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD);
295-
for num_prefix_quads in 0..100 {
296-
for suffix in &["AA", "AAA", "AAAA"] {
297-
let mut prefix = "AAAA".repeat(num_prefix_quads);
298-
prefix.push_str(suffix);
299-
// make sure no overflow (and thus a panic) occurs
300-
let res = engine.decode(prefix);
301-
assert!(res.is_ok());
302-
}
303-
}
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-
}
333349
}

src/engine/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,15 @@ pub trait Engine: Send + Sync {
304304

305305
/// Decode the input into the provided output slice.
306306
///
307+
/// Returns an error if `output` is smaller than the estimated decoded length.
308+
///
307309
/// This will not write any bytes past exactly what is decoded (no stray garbage bytes at the end).
308310
///
309311
/// See [crate::decoded_len_estimate] for calculating buffer sizes.
310312
///
313+
/// See [Engine::decode_slice_unchecked] for a version that panics instead of returning an error
314+
/// if the output buffer is too small.
315+
///
311316
/// # Panics
312317
///
313318
/// Panics if decoded length estimation overflows.
@@ -327,6 +332,35 @@ pub trait Engine: Send + Sync {
327332
self.internal_decode(input_bytes, output, estimate)
328333
.map_err(|e| e.into())
329334
}
335+
336+
/// Decode the input into the provided output slice.
337+
///
338+
/// This will not write any bytes past exactly what is decoded (no stray garbage bytes at the end).
339+
///
340+
/// See [crate::decoded_len_estimate] for calculating buffer sizes.
341+
///
342+
/// See [Engine::decode_slice] for a version that returns an error instead of panicking if the output
343+
/// buffer is too small.
344+
///
345+
/// # Panics
346+
///
347+
/// Panics if decoded length estimation overflows.
348+
/// This would happen for sizes within a few bytes of the maximum value of `usize`.
349+
///
350+
/// Panics if the provided output buffer is too small for the decoded data.
351+
fn decode_slice_unchecked<T: AsRef<[u8]>>(
352+
&self,
353+
input: T,
354+
output: &mut [u8],
355+
) -> Result<usize, DecodeError> {
356+
let input_bytes = input.as_ref();
357+
358+
self.internal_decode(
359+
input_bytes,
360+
output,
361+
self.internal_decoded_len_estimate(input_bytes.len()),
362+
)
363+
}
330364
}
331365

332366
/// The minimal level of configuration that engines must support.

0 commit comments

Comments
 (0)