82 lines
2.7 KiB
Rust
82 lines
2.7 KiB
Rust
use {
|
|
crate::Error,
|
|
std::io::{ErrorKind, Read, Write},
|
|
};
|
|
|
|
pub struct Decoder<R: Read, W: Write> {
|
|
reader: R,
|
|
writer: W,
|
|
}
|
|
|
|
impl<R: Read, W: Write> Decoder<R, W> {
|
|
pub fn new(reader: R, writer: W) -> Self {
|
|
Self { reader, writer }
|
|
}
|
|
|
|
/// Decodes the bytes provided by `self.reader` and writes the resulting
|
|
/// bytes into `self.writer`.
|
|
/// # 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<(), Error> {
|
|
loop {
|
|
let mut in_buf = [0_u8; 2];
|
|
let mut out_buf = [0_u8; 1];
|
|
let mut n_bytes = 0;
|
|
loop {
|
|
n_bytes += match self.reader.read(&mut in_buf) {
|
|
Ok(n) => n,
|
|
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
|
|
Err(e) => return Err(e.into()),
|
|
};
|
|
break;
|
|
}
|
|
match n_bytes {
|
|
0 => break,
|
|
1 => return Err(Error::MissingChar),
|
|
2 => {}
|
|
_ => unreachable!(),
|
|
}
|
|
let mut c = char::from(in_buf[0]);
|
|
if let Some(idx) = super::get_idx(c.to_ascii_uppercase()) {
|
|
out_buf[0] |= (idx << 4) as u8;
|
|
} else {
|
|
return Err(Error::IllegalChar(c));
|
|
}
|
|
c = char::from(in_buf[1]);
|
|
if let Some(idx) = super::get_idx(c.to_ascii_uppercase()) {
|
|
out_buf[0] |= idx as u8;
|
|
} else {
|
|
return Err(Error::IllegalChar(c));
|
|
}
|
|
self.writer.write_all(&out_buf)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn decode() {
|
|
let mut decoder = Decoder::new("48656C6C6F2C20576F726C64".as_bytes(), vec![]);
|
|
decoder.decode().unwrap();
|
|
assert_eq!(String::from_utf8(decoder.writer).unwrap(), "Hello, World");
|
|
decoder = Decoder::new("48656C6C6F2C20576F726C6421".as_bytes(), vec![]);
|
|
decoder.decode().unwrap();
|
|
assert_eq!(String::from_utf8(decoder.writer).unwrap(), "Hello, World!");
|
|
decoder = Decoder::new("48656C6C6F2C20576F726C64210A".as_bytes(), vec![]);
|
|
decoder.decode().unwrap();
|
|
assert_eq!(
|
|
String::from_utf8(decoder.writer).unwrap(),
|
|
"Hello, World!\n"
|
|
);
|
|
}
|
|
}
|