From efb0b531c79581978aec5c410d0ae866b21f8aac Mon Sep 17 00:00:00 2001 From: Marshall Pierce <575695+marshallpierce@users.noreply.github.com> Date: Fri, 21 Oct 2022 07:09:13 -0600 Subject: [PATCH] Don't allocate an unnecessarily large buffer in decode_config Also, address a complaint from `cargo doc`. NB: In practice 1.46.0 is needed to run tests due to dev dependencies, but I'm not addressing any MSRV changes since this will be supplanted by 1.0's higher MSRV anyway. --- Cargo.toml | 2 +- RELEASE-NOTES.md | 4 ++++ src/decode.rs | 22 +++++++++++++++++++++- src/write/encoder.rs | 2 +- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 30e73eec..d3cbf3ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "base64" -version = "0.13.0" +version = "0.13.1" authors = ["Alice Maz ", "Marshall Pierce "] description = "encodes and decodes base64 as bytes or utf8" repository = "https://github.com/marshallpierce/rust-base64" diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 1048c1e8..6a2e3cae 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,7 @@ +# 0.13.1 + +- More precise decode buffer sizing, avoiding unnecessary allocation in `decode_config`. + # 0.13.0 - Config methods are const diff --git a/src/decode.rs b/src/decode.rs index 4cc937d5..e349240a 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -107,7 +107,14 @@ pub fn decode>(input: T) -> Result, DecodeError> { ///``` #[cfg(any(feature = "alloc", feature = "std", test))] pub fn decode_config>(input: T, config: Config) -> Result, DecodeError> { - let mut buffer = Vec::::with_capacity(input.as_ref().len() * 4 / 3); + let decoded_length_estimate = (input + .as_ref() + .len() + .checked_add(3) + .expect("decoded length calculation overflow")) + / 4 + * 3; + let mut buffer = Vec::::with_capacity(decoded_length_estimate); decode_config_buf(input, config, &mut buffer).map(|_| buffer) } @@ -870,4 +877,17 @@ mod tests { } } } + + #[test] + fn decode_config_estimation_works_for_various_lengths() { + for num_prefix_quads in 0..100 { + for suffix in &["AA", "AAA", "AAAA"] { + let mut prefix = "AAAA".repeat(num_prefix_quads); + prefix.push_str(suffix); + // make sure no overflow (and thus a panic) occurs + let res = decode_config(prefix, STANDARD); + assert!(res.is_ok()); + } + } + } } diff --git a/src/write/encoder.rs b/src/write/encoder.rs index 8a48f438..4bb57eb0 100644 --- a/src/write/encoder.rs +++ b/src/write/encoder.rs @@ -223,7 +223,7 @@ impl Write for EncoderWriter { /// Under non-error circumstances, this returns `Ok` with the value being the number of bytes /// of `input` consumed. The value may be `0`, which interacts poorly with `write_all`, which /// interprets `Ok(0)` as an error, despite it being allowed by the contract of `write`. See - /// https://github.com/rust-lang/rust/issues/56889 for more on that. + /// for more on that. /// /// If the previous call to `write` provided more (encoded) data than the delegate writer could /// accept in a single call to its `write`, the remaining data is buffered. As long as buffered