Skip to content

Commit

Permalink
Move decode functions to Engine methods
Browse files Browse the repository at this point in the history
  • Loading branch information
marshallpierce committed Dec 16, 2022
1 parent d0d2c14 commit d00b4db
Show file tree
Hide file tree
Showing 17 changed files with 240 additions and 199 deletions.
5 changes: 2 additions & 3 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
### Breaking changes

- `FastPortable` was only meant to be an interim name, and shouldn't have shipped in 0.20. It is now `GeneralPurpose` to
make its intended usage more
clear.
make its intended usage more clear.
- `GeneralPurpose` and its config are now `pub use`'d in the `engine` module for convenience.
- Change a few `from()` functions to be `new()`. `from()` causes confusing compiler errors because of confusion with `From::from`, and is a little bit misleading because some of those invocations are not very cheap as one would usually expect from a `from` call.
- Change a few `from()` functions to be `new()`. `from()` causes confusing compiler errors because of confusion with `From::from`, and is a little misleading because some of those invocations are not very cheap as one would usually expect from a `from` call.
- `encode*` and `decode*` top level functions are now methods on `Engine`.

# 0.20.0
Expand Down
8 changes: 4 additions & 4 deletions benches/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
extern crate criterion;

use base64::{
decode, decode_engine_slice, decode_engine_vec, display,
display,
engine::{Engine, DEFAULT_ENGINE},
write,
};
Expand All @@ -16,7 +16,7 @@ fn do_decode_bench(b: &mut Bencher, &size: &usize) {
let encoded = DEFAULT_ENGINE.encode(&v);

b.iter(|| {
let orig = decode(&encoded);
let orig = DEFAULT_ENGINE.decode(&encoded);
black_box(&orig);
});
}
Expand All @@ -28,7 +28,7 @@ fn do_decode_bench_reuse_buf(b: &mut Bencher, &size: &usize) {

let mut buf = Vec::new();
b.iter(|| {
decode_engine_vec(&encoded, &mut buf, &DEFAULT_ENGINE).unwrap();
DEFAULT_ENGINE.decode_vec(&encoded, &mut buf).unwrap();
black_box(&buf);
buf.clear();
});
Expand All @@ -42,7 +42,7 @@ fn do_decode_bench_slice(b: &mut Bencher, &size: &usize) {
let mut buf = Vec::new();
buf.resize(size, 0);
b.iter(|| {
decode_engine_slice(&encoded, &mut buf, &DEFAULT_ENGINE).unwrap();
DEFAULT_ENGINE.decode_slice(&encoded, &mut buf).unwrap();
black_box(&buf);
});
}
Expand Down
2 changes: 1 addition & 1 deletion fuzz/fuzzers/decode_random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ fuzz_target!(|data: &[u8]| {

// The data probably isn't valid base64 input, but as long as it returns an error instead
// of crashing, that's correct behavior.
let _ = decode_engine(data, &engine);
let _ = engine.decode(data);
});
2 changes: 1 addition & 1 deletion fuzz/fuzzers/roundtrip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ use base64::{Engine as _, engine::DEFAULT_ENGINE};

fuzz_target!(|data: &[u8]| {
let encoded = DEFAULT_ENGINE.encode(data);
let decoded = base64::decode_engine(&encoded, &DEFAULT_ENGINE).unwrap();
let decoded = DEFAULT_ENGINE.decode(&encoded).unwrap();
assert_eq!(data, decoded.as_slice());
});
2 changes: 1 addition & 1 deletion fuzz/fuzzers/roundtrip_no_pad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ fuzz_target!(|data: &[u8]| {
let engine = general_purpose::GeneralPurpose::new(&base64::alphabet::STANDARD, config);

let encoded = engine.encode(data);
let decoded = base64::decode_engine(&encoded, &engine).unwrap();
let decoded = engine.decode(&encoded).unwrap();
assert_eq!(data, decoded.as_slice());
});
2 changes: 1 addition & 1 deletion fuzz/fuzzers/roundtrip_random_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ fuzz_target!(|data: &[u8]| {
let engine = utils::random_engine(data);

let encoded = engine.encode(data);
let decoded = decode_engine(&encoded, &engine).unwrap();
let decoded = engine.decode(&encoded).unwrap();
assert_eq!(data, decoded.as_slice());
});
138 changes: 27 additions & 111 deletions src/decode.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#[cfg(any(feature = "alloc", feature = "std", test))]
use crate::engine::DecodeEstimate;
use crate::engine::Engine;
#[cfg(any(feature = "alloc", feature = "std", test))]
use crate::engine::DEFAULT_ENGINE;
Expand Down Expand Up @@ -61,138 +59,50 @@ impl error::Error for DecodeError {
}

///Decode base64 using the [default engine](DEFAULT_ENGINE).
///Returns a `Result` containing a `Vec<u8>`.
///
///# Example
///
///```rust
/// let bytes = base64::decode("aGVsbG8gd29ybGQ=").unwrap();
/// println!("{:?}", bytes);
///```
/// See [Engine::decode].
#[deprecated(since = "0.21.0", note = "Use Engine::decode")]
#[cfg(any(feature = "alloc", feature = "std", test))]
pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> {
decode_engine(input, &DEFAULT_ENGINE)
DEFAULT_ENGINE.decode(input)
}

///Decode from string reference as octets using the specified [Engine].
///Returns a `Result` containing a `Vec<u8>`.
///
///# Example
///
///```rust
/// let bytes = base64::decode_engine(
/// "aGVsbG8gd29ybGR+Cg==",
/// &base64::engine::DEFAULT_ENGINE,
/// ).unwrap();
/// println!("{:?}", bytes);
///
/// // custom engine setup
/// let bytes_url = base64::decode_engine(
/// "aGVsbG8gaW50ZXJuZXR-Cg",
/// &base64::engine::GeneralPurpose::new(
/// &base64::alphabet::URL_SAFE,
/// base64::engine::general_purpose::NO_PAD),
///
/// ).unwrap();
/// println!("{:?}", bytes_url);
///```
/// See [Engine::decode].
///Returns a `Result` containing a `Vec<u8>`.
#[deprecated(since = "0.21.0", note = "Use Engine::decode")]
#[cfg(any(feature = "alloc", feature = "std", test))]
pub fn decode_engine<E: Engine, T: AsRef<[u8]>>(
input: T,
engine: &E,
) -> Result<Vec<u8>, DecodeError> {
let decoded_length_estimate = (input
.as_ref()
.len()
.checked_add(3)
.expect("decoded length calculation overflow"))
/ 4
* 3;
let mut buffer = Vec::<u8>::with_capacity(decoded_length_estimate);
decode_engine_vec(input, &mut buffer, engine).map(|_| buffer)
engine.decode(input)
}

///Decode from string reference as octets.
///Writes into the supplied `Vec`, which may allocate if its internal buffer isn't big enough.
///Returns a `Result` containing an empty tuple, aka `()`.
///
///# Example
///
///```rust
///const URL_SAFE_ENGINE: base64::engine::GeneralPurpose =
/// base64::engine::GeneralPurpose::new(
/// &base64::alphabet::URL_SAFE,
/// base64::engine::general_purpose::PAD);
///
///fn main() {
/// let mut buffer = Vec::<u8>::new();
/// // with the default engine
/// base64::decode_engine_vec(
/// "aGVsbG8gd29ybGR+Cg==",
/// &mut buffer,
/// &base64::engine::DEFAULT_ENGINE
/// ).unwrap();
/// println!("{:?}", buffer);
/// Decode from string reference as octets.
///
/// buffer.clear();
///
/// // with a custom engine
/// base64::decode_engine_vec(
/// "aGVsbG8gaW50ZXJuZXR-Cg==",
/// &mut buffer,
/// &URL_SAFE_ENGINE
/// ).unwrap();
/// println!("{:?}", buffer);
///}
///```
/// See [Engine::decode_vec].
#[cfg(any(feature = "alloc", feature = "std", test))]
#[deprecated(since = "0.21.0", note = "Use Engine::decode_vec")]
pub fn decode_engine_vec<E: Engine, T: AsRef<[u8]>>(
input: T,
buffer: &mut Vec<u8>,
engine: &E,
) -> Result<(), DecodeError> {
let input_bytes = input.as_ref();

let starting_output_len = buffer.len();

let estimate = engine.decoded_length_estimate(input_bytes.len());
let total_len_estimate = estimate
.decoded_length_estimate()
.checked_add(starting_output_len)
.expect("Overflow when calculating output buffer length");
buffer.resize(total_len_estimate, 0);

let buffer_slice = &mut buffer.as_mut_slice()[starting_output_len..];
let bytes_written = engine.decode(input_bytes, buffer_slice, estimate)?;

buffer.truncate(starting_output_len + bytes_written);

Ok(())
engine.decode_vec(input, buffer)
}

/// Decode the input into the provided output slice.
///
/// This will not write any bytes past exactly what is decoded (no stray garbage bytes at the end).
///
/// If you don't know ahead of time what the decoded length should be, size your buffer with a
/// conservative estimate for the decoded length of an input: 3 bytes of output for every 4 bytes of
/// input, rounded up, or in other words `(input_len + 3) / 4 * 3`.
///
/// # Panics
///
/// If the slice is not large enough, this will panic.
/// See [Engine::decode_slice].
#[deprecated(since = "0.21.0", note = "Use Engine::decode_slice")]
pub fn decode_engine_slice<E: Engine, T: AsRef<[u8]>>(
input: T,
output: &mut [u8],
engine: &E,
) -> Result<usize, DecodeError> {
let input_bytes = input.as_ref();

engine.decode(
input_bytes,
output,
engine.decoded_length_estimate(input_bytes.len()),
)
engine.decode_slice(input, output)
}

#[cfg(test)]
Expand Down Expand Up @@ -249,9 +159,13 @@ mod tests {
decoded_with_prefix.copy_from_slice(&prefix);

// decode into the non-empty buf
decode_engine_vec(&encoded_data, &mut decoded_with_prefix, &engine).unwrap();
engine
.decode_vec(&encoded_data, &mut decoded_with_prefix)
.unwrap();
// also decode into the empty buf
decode_engine_vec(&encoded_data, &mut decoded_without_prefix, &engine).unwrap();
engine
.decode_vec(&encoded_data, &mut decoded_without_prefix)
.unwrap();

assert_eq!(
prefix_len + decoded_without_prefix.len(),
Expand Down Expand Up @@ -304,8 +218,9 @@ mod tests {
let offset = 1000;

// decode into the non-empty buf
let decode_bytes_written =
decode_engine_slice(&encoded_data, &mut decode_buf[offset..], &engine).unwrap();
let decode_bytes_written = engine
.decode_slice(&encoded_data, &mut decode_buf[offset..])
.unwrap();

assert_eq!(orig_data.len(), decode_bytes_written);
assert_eq!(
Expand Down Expand Up @@ -347,8 +262,9 @@ mod tests {
decode_buf.resize(input_len, 0);

// decode into the non-empty buf
let decode_bytes_written =
decode_engine_slice(&encoded_data, &mut decode_buf[..], &engine).unwrap();
let decode_bytes_written = engine
.decode_slice(&encoded_data, &mut decode_buf[..])
.unwrap();

assert_eq!(orig_data.len(), decode_bytes_written);
assert_eq!(orig_data, decode_buf);
Expand All @@ -363,7 +279,7 @@ mod tests {
let mut prefix = "AAAA".repeat(num_prefix_quads);
prefix.push_str(suffix);
// make sure no overflow (and thus a panic) occurs
let res = decode_engine(prefix, &engine);
let res = engine.decode(prefix);
assert!(res.is_ok());
}
}
Expand Down
13 changes: 9 additions & 4 deletions src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ mod tests {

use crate::{
alphabet::{IMAP_MUTF7, STANDARD, URL_SAFE},
decode::decode_engine_vec,
engine::{
general_purpose::{GeneralPurpose, NO_PAD},
DEFAULT_ENGINE,
Expand Down Expand Up @@ -262,7 +261,9 @@ mod tests {

assert_eq!(prefix, encoded_data_with_prefix);

decode_engine_vec(&encoded_data_no_prefix, &mut decoded, &engine).unwrap();
engine
.decode_vec(&encoded_data_no_prefix, &mut decoded)
.unwrap();
assert_eq!(orig_data, decoded);
}
}
Expand Down Expand Up @@ -317,7 +318,9 @@ mod tests {
&encoded_data_original_state[encoded_size..]
);

decode_engine_vec(&encoded_data[0..encoded_size], &mut decoded, &engine).unwrap();
engine
.decode_vec(&encoded_data[0..encoded_size], &mut decoded)
.unwrap();
assert_eq!(orig_data, decoded);
}
}
Expand Down Expand Up @@ -360,7 +363,9 @@ mod tests {
input_len,
);

decode_engine_vec(&encoded_data[0..encoded_size], &mut decoded, &engine).unwrap();
engine
.decode_vec(&encoded_data[0..encoded_size], &mut decoded)
.unwrap();
assert_eq!(orig_data, decoded);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/engine/general_purpose/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl super::Engine for GeneralPurpose {
GeneralPurposeEstimate::new(input_len)
}

fn decode(
fn inner_decode(
&self,
input: &[u8],
output: &mut [u8],
Expand Down
Loading

0 comments on commit d00b4db

Please sign in to comment.