use { crate::Error, std::io::{ErrorKind, Read, Write}, }; pub struct Decoder { reader: R, writer: W, } impl Decoder { 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" ); } }