summaryrefslogtreecommitdiffstats
path: root/third_party/rust/base64/src/decode.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /third_party/rust/base64/src/decode.rs
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/base64/src/decode.rs')
-rw-r--r--third_party/rust/base64/src/decode.rs349
1 files changed, 349 insertions, 0 deletions
diff --git a/third_party/rust/base64/src/decode.rs b/third_party/rust/base64/src/decode.rs
new file mode 100644
index 0000000000..047151840c
--- /dev/null
+++ b/third_party/rust/base64/src/decode.rs
@@ -0,0 +1,349 @@
+use crate::engine::{general_purpose::STANDARD, DecodeEstimate, Engine};
+#[cfg(any(feature = "alloc", feature = "std", test))]
+use alloc::vec::Vec;
+use core::fmt;
+#[cfg(any(feature = "std", test))]
+use std::error;
+
+/// Errors that can occur while decoding.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum DecodeError {
+ /// An invalid byte was found in the input. The offset and offending byte are provided.
+ /// Padding characters (`=`) interspersed in the encoded form will be treated as invalid bytes.
+ InvalidByte(usize, u8),
+ /// The length of the input is invalid.
+ /// A typical cause of this is stray trailing whitespace or other separator bytes.
+ /// In the case where excess trailing bytes have produced an invalid length *and* the last byte
+ /// is also an invalid base64 symbol (as would be the case for whitespace, etc), `InvalidByte`
+ /// will be emitted instead of `InvalidLength` to make the issue easier to debug.
+ InvalidLength,
+ /// The last non-padding input symbol's encoded 6 bits have nonzero bits that will be discarded.
+ /// This is indicative of corrupted or truncated Base64.
+ /// Unlike `InvalidByte`, which reports symbols that aren't in the alphabet, this error is for
+ /// symbols that are in the alphabet but represent nonsensical encodings.
+ InvalidLastSymbol(usize, u8),
+ /// The nature of the padding was not as configured: absent or incorrect when it must be
+ /// canonical, or present when it must be absent, etc.
+ InvalidPadding,
+}
+
+impl fmt::Display for DecodeError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Self::InvalidByte(index, byte) => write!(f, "Invalid byte {}, offset {}.", byte, index),
+ Self::InvalidLength => write!(f, "Encoded text cannot have a 6-bit remainder."),
+ Self::InvalidLastSymbol(index, byte) => {
+ write!(f, "Invalid last symbol {}, offset {}.", byte, index)
+ }
+ Self::InvalidPadding => write!(f, "Invalid padding"),
+ }
+ }
+}
+
+#[cfg(any(feature = "std", test))]
+impl error::Error for DecodeError {
+ fn cause(&self) -> Option<&dyn error::Error> {
+ None
+ }
+}
+
+/// Errors that can occur while decoding into a slice.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum DecodeSliceError {
+ /// A [DecodeError] occurred
+ DecodeError(DecodeError),
+ /// The provided slice _may_ be too small.
+ ///
+ /// The check is conservative (assumes the last triplet of output bytes will all be needed).
+ OutputSliceTooSmall,
+}
+
+impl fmt::Display for DecodeSliceError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::DecodeError(e) => write!(f, "DecodeError: {}", e),
+ Self::OutputSliceTooSmall => write!(f, "Output slice too small"),
+ }
+ }
+}
+
+#[cfg(any(feature = "std", test))]
+impl error::Error for DecodeSliceError {
+ fn cause(&self) -> Option<&dyn error::Error> {
+ match self {
+ DecodeSliceError::DecodeError(e) => Some(e),
+ DecodeSliceError::OutputSliceTooSmall => None,
+ }
+ }
+}
+
+impl From<DecodeError> for DecodeSliceError {
+ fn from(e: DecodeError) -> Self {
+ DecodeSliceError::DecodeError(e)
+ }
+}
+
+/// Decode base64 using the [`STANDARD` engine](STANDARD).
+///
+/// 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> {
+ STANDARD.decode(input)
+}
+
+/// Decode from string reference as octets using the specified [Engine].
+///
+/// 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> {
+ engine.decode(input)
+}
+
+/// Decode from string reference as octets.
+///
+/// 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> {
+ engine.decode_vec(input, buffer)
+}
+
+/// Decode the input into the provided output slice.
+///
+/// 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, DecodeSliceError> {
+ engine.decode_slice(input, output)
+}
+
+/// Returns a conservative estimate of the decoded size of `encoded_len` base64 symbols (rounded up
+/// to the next group of 3 decoded bytes).
+///
+/// The resulting length will be a safe choice for the size of a decode buffer, but may have up to
+/// 2 trailing bytes that won't end up being needed.
+///
+/// # Examples
+///
+/// ```
+/// use base64::decoded_len_estimate;
+///
+/// assert_eq!(3, decoded_len_estimate(1));
+/// assert_eq!(3, decoded_len_estimate(2));
+/// assert_eq!(3, decoded_len_estimate(3));
+/// assert_eq!(3, decoded_len_estimate(4));
+/// // start of the next quad of encoded symbols
+/// assert_eq!(6, decoded_len_estimate(5));
+/// ```
+///
+/// # Panics
+///
+/// Panics if decoded length estimation overflows.
+/// This would happen for sizes within a few bytes of the maximum value of `usize`.
+pub fn decoded_len_estimate(encoded_len: usize) -> usize {
+ STANDARD
+ .internal_decoded_len_estimate(encoded_len)
+ .decoded_len_estimate()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::{
+ alphabet,
+ engine::{general_purpose, Config, GeneralPurpose},
+ tests::{assert_encode_sanity, random_engine},
+ };
+ use rand::{
+ distributions::{Distribution, Uniform},
+ Rng, SeedableRng,
+ };
+
+ #[test]
+ fn decode_into_nonempty_vec_doesnt_clobber_existing_prefix() {
+ let mut orig_data = Vec::new();
+ let mut encoded_data = String::new();
+ let mut decoded_with_prefix = Vec::new();
+ let mut decoded_without_prefix = Vec::new();
+ let mut prefix = Vec::new();
+
+ let prefix_len_range = Uniform::new(0, 1000);
+ let input_len_range = Uniform::new(0, 1000);
+
+ let mut rng = rand::rngs::SmallRng::from_entropy();
+
+ for _ in 0..10_000 {
+ orig_data.clear();
+ encoded_data.clear();
+ decoded_with_prefix.clear();
+ decoded_without_prefix.clear();
+ prefix.clear();
+
+ let input_len = input_len_range.sample(&mut rng);
+
+ for _ in 0..input_len {
+ orig_data.push(rng.gen());
+ }
+
+ let engine = random_engine(&mut rng);
+ engine.encode_string(&orig_data, &mut encoded_data);
+ assert_encode_sanity(&encoded_data, engine.config().encode_padding(), input_len);
+
+ let prefix_len = prefix_len_range.sample(&mut rng);
+
+ // fill the buf with a prefix
+ for _ in 0..prefix_len {
+ prefix.push(rng.gen());
+ }
+
+ decoded_with_prefix.resize(prefix_len, 0);
+ decoded_with_prefix.copy_from_slice(&prefix);
+
+ // decode into the non-empty buf
+ engine
+ .decode_vec(&encoded_data, &mut decoded_with_prefix)
+ .unwrap();
+ // also decode into the empty buf
+ engine
+ .decode_vec(&encoded_data, &mut decoded_without_prefix)
+ .unwrap();
+
+ assert_eq!(
+ prefix_len + decoded_without_prefix.len(),
+ decoded_with_prefix.len()
+ );
+ assert_eq!(orig_data, decoded_without_prefix);
+
+ // append plain decode onto prefix
+ prefix.append(&mut decoded_without_prefix);
+
+ assert_eq!(prefix, decoded_with_prefix);
+ }
+ }
+
+ #[test]
+ fn decode_slice_doesnt_clobber_existing_prefix_or_suffix() {
+ do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| {
+ e.decode_slice(input, output).unwrap()
+ })
+ }
+
+ #[test]
+ fn decode_slice_unchecked_doesnt_clobber_existing_prefix_or_suffix() {
+ do_decode_slice_doesnt_clobber_existing_prefix_or_suffix(|e, input, output| {
+ e.decode_slice_unchecked(input, output).unwrap()
+ })
+ }
+
+ #[test]
+ fn decode_engine_estimation_works_for_various_lengths() {
+ let engine = GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD);
+ 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 = engine.decode(prefix);
+ assert!(res.is_ok());
+ }
+ }
+ }
+
+ #[test]
+ fn decode_slice_output_length_errors() {
+ for num_quads in 1..100 {
+ let input = "AAAA".repeat(num_quads);
+ let mut vec = vec![0; (num_quads - 1) * 3];
+ assert_eq!(
+ DecodeSliceError::OutputSliceTooSmall,
+ STANDARD.decode_slice(&input, &mut vec).unwrap_err()
+ );
+ vec.push(0);
+ assert_eq!(
+ DecodeSliceError::OutputSliceTooSmall,
+ STANDARD.decode_slice(&input, &mut vec).unwrap_err()
+ );
+ vec.push(0);
+ assert_eq!(
+ DecodeSliceError::OutputSliceTooSmall,
+ STANDARD.decode_slice(&input, &mut vec).unwrap_err()
+ );
+ vec.push(0);
+ // now it works
+ assert_eq!(
+ num_quads * 3,
+ STANDARD.decode_slice(&input, &mut vec).unwrap()
+ );
+ }
+ }
+
+ fn do_decode_slice_doesnt_clobber_existing_prefix_or_suffix<
+ F: Fn(&GeneralPurpose, &[u8], &mut [u8]) -> usize,
+ >(
+ call_decode: F,
+ ) {
+ let mut orig_data = Vec::new();
+ let mut encoded_data = String::new();
+ let mut decode_buf = Vec::new();
+ let mut decode_buf_copy: Vec<u8> = Vec::new();
+
+ let input_len_range = Uniform::new(0, 1000);
+
+ let mut rng = rand::rngs::SmallRng::from_entropy();
+
+ for _ in 0..10_000 {
+ orig_data.clear();
+ encoded_data.clear();
+ decode_buf.clear();
+ decode_buf_copy.clear();
+
+ let input_len = input_len_range.sample(&mut rng);
+
+ for _ in 0..input_len {
+ orig_data.push(rng.gen());
+ }
+
+ let engine = random_engine(&mut rng);
+ engine.encode_string(&orig_data, &mut encoded_data);
+ assert_encode_sanity(&encoded_data, engine.config().encode_padding(), input_len);
+
+ // fill the buffer with random garbage, long enough to have some room before and after
+ for _ in 0..5000 {
+ decode_buf.push(rng.gen());
+ }
+
+ // keep a copy for later comparison
+ decode_buf_copy.extend(decode_buf.iter());
+
+ let offset = 1000;
+
+ // decode into the non-empty buf
+ let decode_bytes_written =
+ call_decode(&engine, encoded_data.as_bytes(), &mut decode_buf[offset..]);
+
+ assert_eq!(orig_data.len(), decode_bytes_written);
+ assert_eq!(
+ orig_data,
+ &decode_buf[offset..(offset + decode_bytes_written)]
+ );
+ assert_eq!(&decode_buf_copy[0..offset], &decode_buf[0..offset]);
+ assert_eq!(
+ &decode_buf_copy[offset + decode_bytes_written..],
+ &decode_buf[offset + decode_bytes_written..]
+ );
+ }
+ }
+}