Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions base64ct/src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ use crate::{
};
use core::{cmp, marker::PhantomData};

#[cfg(feature = "alloc")]
use {alloc::vec::Vec, core::iter};

#[cfg(feature = "std")]
use std::io;

#[cfg(docsrs)]
use crate::{Base64, Base64Unpadded};

Expand Down Expand Up @@ -165,6 +171,27 @@ impl<'i, E: Variant> Decoder<'i, E> {
Ok(out)
}

/// Decode all remaining Base64 data, placing the result into `buf`.
///
/// If successful, this function will return the total number of bytes
/// decoded into `buf`.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn decode_to_end<'o>(&mut self, buf: &'o mut Vec<u8>) -> Result<&'o [u8], Error> {
let start_len = buf.len();
let decoded_len = self.decoded_len();
let total_len = start_len + decoded_len;

if total_len > buf.capacity() {
buf.reserve(total_len - buf.capacity());
}

// Append `decoded_len` zeroes to the vector
buf.extend(iter::repeat(0).take(decoded_len));
self.decode(&mut buf[start_len..])?;
Ok(&buf[start_len..])
}

/// Get the length of the remaining data after Base64 decoding.
///
/// Decreases every time data is decoded.
Expand Down Expand Up @@ -230,6 +257,29 @@ impl<'i, E: Variant> Decoder<'i, E> {
}
}

#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<'i, E: Variant> io::Read for Decoder<'i, E> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let slice = match buf.get_mut(..self.decoded_len()) {
Some(bytes) => bytes,
None => buf,
};

self.decode(slice)?;
Ok(slice.len())
}

fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
Ok(self.decode_to_end(buf)?.len())
}

fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.decode(buf)?;
Ok(())
}
}

/// Base64 decode buffer for a 1-block input.
///
/// This handles a partially decoded block of data, i.e. data which has been
Expand Down Expand Up @@ -501,6 +551,9 @@ impl<'i> Iterator for LineReader<'i> {
mod tests {
use crate::{test_vectors::*, variant::Variant, Base64, Base64Unpadded, Decoder};

#[cfg(feature = "std")]
use {alloc::vec::Vec, std::io::Read};

#[test]
fn decode_padded() {
decode_test(PADDED_BIN, || {
Expand Down Expand Up @@ -530,6 +583,19 @@ mod tests {
})
}

#[cfg(feature = "std")]
#[test]
fn read_multiline_padded() {
let mut decoder =
Decoder::<Base64>::new_wrapped(MULTILINE_PADDED_BASE64.as_bytes(), 70).unwrap();

let mut buf = Vec::new();
let len = decoder.read_to_end(&mut buf).unwrap();

assert_eq!(len, MULTILINE_PADDED_BIN.len());
assert_eq!(buf.as_slice(), MULTILINE_PADDED_BIN);
}

/// Core functionality of a decoding test
fn decode_test<'a, F, V>(expected: &[u8], f: F)
where
Expand Down
3 changes: 3 additions & 0 deletions base64ct/src/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use crate::{
};
use core::{cmp, marker::PhantomData, str};

#[cfg(docsrs)]
use crate::{Base64, Base64Unpadded};

/// Stateful Base64 encoder with support for buffered, incremental encoding.
///
/// The `E` type parameter can be any type which impls [`Encoding`] such as
Expand Down
10 changes: 10 additions & 0 deletions base64ct/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,14 @@ impl From<core::str::Utf8Error> for Error {
}

#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl From<Error> for std::io::Error {
fn from(err: Error) -> std::io::Error {
// TODO(tarcieri): better customize `ErrorKind`?
std::io::Error::new(std::io::ErrorKind::InvalidData, err)
}
}

#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for Error {}