Some polish and adjustment to make the interface and code closer to the
corresponding b64 crate
This commit is contained in:
parent
0d1f59bf6c
commit
0ce411a7fd
2 changed files with 46 additions and 27 deletions
|
@ -1,17 +1,7 @@
|
||||||
use super::*;
|
use {
|
||||||
use std::io::{self, ErrorKind, Read, Write};
|
crate::{error::Error, B32Alphabet},
|
||||||
|
std::io::{ErrorKind, Read, Write},
|
||||||
#[derive(Debug)]
|
};
|
||||||
pub enum DecoderError {
|
|
||||||
IO(io::Error),
|
|
||||||
IllegalChar,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for DecoderError {
|
|
||||||
fn from(value: io::Error) -> Self {
|
|
||||||
Self::IO(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Decoder<R: Read, W: Write> {
|
pub struct Decoder<R: Read, W: Write> {
|
||||||
reader: R,
|
reader: R,
|
||||||
|
@ -30,7 +20,22 @@ impl<R: Read, W: Write> Decoder<R, W> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode(mut self) -> Result<W, DecoderError> {
|
|
||||||
|
/// Decodes the bytes provided by `self.reader` and writes the resulting
|
||||||
|
/// bytes into `self.writer`.
|
||||||
|
/// # Important
|
||||||
|
/// Both reader and writer should be buffered for good performance, as the
|
||||||
|
/// process of decoding will perform a series of very short read and write
|
||||||
|
/// operations.
|
||||||
|
/// # Errors
|
||||||
|
/// - io failure will return `Error::Io`
|
||||||
|
/// - a character not in the alphabet, or encountering any character after
|
||||||
|
/// the first occurance of the padding character, will return `Error::IllegalChar`
|
||||||
|
/// - if the reader comes up short of the final four byte block will return
|
||||||
|
/// `Error::MissingPadding`
|
||||||
|
/// - `intError` should never be returned. If this error is recieved the code
|
||||||
|
/// is somehow broken. Please file a bug.
|
||||||
|
pub fn decode(mut self) -> Result<W, Error> {
|
||||||
let mut byte_reader = self.reader.bytes();
|
let mut byte_reader = self.reader.bytes();
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
let mut in_buf = [0_u8; 8];
|
let mut in_buf = [0_u8; 8];
|
||||||
|
@ -58,7 +63,7 @@ impl<R: Read, W: Write> Decoder<R, W> {
|
||||||
for c in &in_buf {
|
for c in &in_buf {
|
||||||
num <<= 5;
|
num <<= 5;
|
||||||
if !matches!(self.alphabet.pad(), Some(ch) if ch == *c) {
|
if !matches!(self.alphabet.pad(), Some(ch) if ch == *c) {
|
||||||
let idx = self.alphabet.idx(*c).ok_or(DecoderError::IllegalChar)?;
|
let idx = self.alphabet.idx(*c).ok_or(Error::IllegalChar((*c).into()))?;
|
||||||
num |= idx as u64;
|
num |= idx as u64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,6 +88,11 @@ impl<R: Read, W: Write> Decoder<R, W> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::B32_RFC4648_ALPHABET;
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{BufReader, Read},
|
||||||
|
};
|
||||||
|
|
||||||
static HELLO: &'static str = "Hello, World!";
|
static HELLO: &'static str = "Hello, World!";
|
||||||
static ENCODED: &'static str = "JBSWY3DPFQQFO33SNRSCC===";
|
static ENCODED: &'static str = "JBSWY3DPFQQFO33SNRSCC===";
|
||||||
|
@ -98,7 +108,25 @@ mod tests {
|
||||||
let reader = ENCODED.as_bytes();
|
let reader = ENCODED.as_bytes();
|
||||||
let writer = Vec::<u8>::new();
|
let writer = Vec::<u8>::new();
|
||||||
let decoder = Decoder::new(reader, writer, None, false);
|
let decoder = Decoder::new(reader, writer, None, false);
|
||||||
let output = decoder.decode().unwrap();
|
assert_eq!(decoder.decode().unwrap(), HELLO.as_bytes());
|
||||||
assert_eq!(HELLO.as_bytes(), output);
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ignore_whitespace() {
|
||||||
|
let decoder = Decoder::new(" JBSWY3DP\tFQQFO33SNRSCC===".as_bytes(), vec![], None, true);
|
||||||
|
assert_eq!(decoder.decode().unwrap(), HELLO.as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decode_wrapped() {
|
||||||
|
let infile = File::open("src/testdata/lorem_b32.txt").unwrap();
|
||||||
|
let reader = BufReader::new(infile);
|
||||||
|
let mut outfile = vec![];
|
||||||
|
File::open("src/testdata/lorem.txt")
|
||||||
|
.unwrap()
|
||||||
|
.read_to_end(&mut outfile)
|
||||||
|
.unwrap();
|
||||||
|
let decoder = Decoder::new(reader, vec![], None, false);
|
||||||
|
assert_eq!(decoder.decode().unwrap(), outfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::{error, fmt, io, num::TryFromIntError};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Fmt(fmt::Error),
|
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
IllegalChar(char),
|
IllegalChar(char),
|
||||||
IntError(TryFromIntError),
|
IntError(TryFromIntError),
|
||||||
|
@ -12,7 +11,6 @@ pub enum Error {
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Fmt(e) => write!(f, "{e}"),
|
|
||||||
Self::Io(e) => write!(f, "{e}"),
|
Self::Io(e) => write!(f, "{e}"),
|
||||||
Self::IllegalChar(c) => write!(f, "Illegal Character ({c})"),
|
Self::IllegalChar(c) => write!(f, "Illegal Character ({c})"),
|
||||||
Self::MissingPadding => write!(f, "Missing Padding"),
|
Self::MissingPadding => write!(f, "Missing Padding"),
|
||||||
|
@ -21,12 +19,6 @@ impl fmt::Display for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<fmt::Error> for Error {
|
|
||||||
fn from(value: fmt::Error) -> Self {
|
|
||||||
Self::Fmt(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl From<io::Error> for Error {
|
||||||
fn from(value: io::Error) -> Self {
|
fn from(value: io::Error) -> Self {
|
||||||
Self::Io(value)
|
Self::Io(value)
|
||||||
|
@ -48,7 +40,6 @@ impl From<TryFromIntError> for Error {
|
||||||
impl error::Error for Error {
|
impl error::Error for Error {
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
match self {
|
match self {
|
||||||
Self::Fmt(e) => Some(e),
|
|
||||||
Self::Io(e) => Some(e),
|
Self::Io(e) => Some(e),
|
||||||
Self::IntError(e) => Some(e),
|
Self::IntError(e) => Some(e),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
Loading…
Add table
Reference in a new issue