Add decode function and tests
TODO: Fix test failure in encoding with incorrect padding calculation
This commit is contained in:
parent
e12cc66fba
commit
2236a572f7
@ -7,6 +7,7 @@ pub use {
|
||||
pub enum Error {
|
||||
Io(io::Error),
|
||||
IllegalChar(char),
|
||||
MissingPadding,
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
@ -27,7 +28,78 @@ pub struct Decoder<R: Read, W: Write> {
|
||||
alphabet: B64Alphabet,
|
||||
}
|
||||
|
||||
impl<R: Read, W: Write> Decoder<R, W> {
|
||||
pub fn new(reader: R, writer: W, alphabet: Option<B64Alphabet>) -> Self {
|
||||
Self {
|
||||
reader,
|
||||
writer,
|
||||
alphabet: alphabet.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode(&mut self) -> Result<(), Error> {
|
||||
loop {
|
||||
let mut ibuf = [0_u8; 4];
|
||||
let mut obuf = [0_u8; 3];
|
||||
let mut num = 0_u32;
|
||||
let mut n_bytes = 0;
|
||||
loop {
|
||||
n_bytes += match self.reader.read(&mut ibuf) {
|
||||
Ok(n) => n,
|
||||
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
break;
|
||||
}
|
||||
match n_bytes {
|
||||
0 => break,
|
||||
4 => {}
|
||||
_ => return Err(Error::MissingPadding),
|
||||
}
|
||||
let mut bytes = 0;
|
||||
for (i, &c) in ibuf.iter().enumerate() {
|
||||
let c = c.into();
|
||||
num <<= 6;
|
||||
if c == self.alphabet.pad() {
|
||||
continue;
|
||||
}
|
||||
if i != bytes {
|
||||
return Err(Error::IllegalChar(c));
|
||||
}
|
||||
let Some(idx) = self.alphabet.get(c) else {
|
||||
return Err(Error::IllegalChar(c));
|
||||
};
|
||||
num |= idx as u32;
|
||||
bytes += 1;
|
||||
}
|
||||
let olen = bytes * 6 / 8;
|
||||
for i in (0..3).rev() {
|
||||
obuf[i] = (num & 0xff) as u8;
|
||||
num >>= 8;
|
||||
}
|
||||
self.writer.write_all(&mut obuf[0..olen])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn decode() {
|
||||
let mut decoder = Decoder::new("SGVsbG8sIFdvcmxk".as_bytes(), vec![], None);
|
||||
decoder.decode().unwrap();
|
||||
assert_eq!(String::from_utf8(decoder.writer).unwrap(), "Hello, World");
|
||||
decoder = Decoder::new("SGVsbG8sIFdvcmxkIQ==".as_bytes(), vec![], None);
|
||||
decoder.decode().unwrap();
|
||||
assert_eq!(String::from_utf8(decoder.writer).unwrap(), "Hello, World!");
|
||||
decoder = Decoder::new("SGVsbG8sIFdvcmxkIQo=".as_bytes(), vec![], None);
|
||||
decoder.decode().unwrap();
|
||||
assert_eq!(
|
||||
String::from_utf8(decoder.writer).unwrap(),
|
||||
"Hello, World!\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -49,11 +49,11 @@ pub struct Encoder<R: Read, W: Write> {
|
||||
}
|
||||
|
||||
impl<R: Read, W: Write> Encoder<R, W> {
|
||||
pub fn new(reader: R, writer: W, alphabet: B64Alphabet) -> Self {
|
||||
pub fn new(reader: R, writer: W, alphabet: Option<B64Alphabet>) -> Self {
|
||||
Self {
|
||||
reader,
|
||||
writer,
|
||||
alphabet,
|
||||
alphabet: alphabet.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,12 +116,18 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn encode() {
|
||||
let mut encoder = Encoder {
|
||||
let mut encoder = Encoder::new("Hello, World".as_bytes(), String::new(), None);
|
||||
encoder.encode().unwrap();
|
||||
assert_eq!(encoder.output(), "SGVsbG8sIFdvcmxk");
|
||||
encoder = Encoder {
|
||||
reader: "Hello, World!".as_bytes(),
|
||||
writer: String::new(),
|
||||
alphabet: B64Alphabet::default(),
|
||||
};
|
||||
encoder.encode().unwrap();
|
||||
assert_eq!(encoder.output(), "SGVsbG8sIFdvcmxkIQ==");
|
||||
encoder = Encoder::new("Hello, World!\n".as_bytes(), String::new(), None);
|
||||
encoder.encode().unwrap();
|
||||
assert_eq!(encoder.output(), "SGVsbG8sIFdvcmxkIQo=");
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ impl Default for B64Alphabet {
|
||||
}
|
||||
|
||||
impl B64Alphabet {
|
||||
pub fn idx(&self, c: char) -> Option<usize> {
|
||||
pub fn get(&self, c: char) -> Option<usize> {
|
||||
for (idx, x) in self.items.iter().enumerate() {
|
||||
if *x == c {
|
||||
return Some(idx);
|
||||
|
Loading…
Reference in New Issue
Block a user