136 lines
3.5 KiB
Rust
136 lines
3.5 KiB
Rust
pub use {
|
|
super::B64Alphabet,
|
|
std::{
|
|
fmt::{self, Write},
|
|
io::{self, ErrorKind, Read},
|
|
},
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
Io(io::Error),
|
|
Fmt(fmt::Error),
|
|
}
|
|
|
|
impl fmt::Display for Error {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Io(e) => write!(f, "{e}"),
|
|
Self::Fmt(e) => write!(f, "{e}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for Error {
|
|
fn from(value: io::Error) -> Self {
|
|
Self::Io(value)
|
|
}
|
|
}
|
|
|
|
impl From<fmt::Error> for Error {
|
|
fn from(value: fmt::Error) -> Self {
|
|
Self::Fmt(value)
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for Error {
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
match self {
|
|
Self::Fmt(e) => Some(e),
|
|
Self::Io(e) => Some(e),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Encoder<R: Read, W: Write> {
|
|
reader: R,
|
|
writer: W,
|
|
alphabet: B64Alphabet,
|
|
}
|
|
|
|
impl<R: Read, W: Write> Encoder<R, W> {
|
|
pub fn new(reader: R, writer: W, alphabet: Option<B64Alphabet>) -> Self {
|
|
Self {
|
|
reader,
|
|
writer,
|
|
alphabet: alphabet.unwrap_or_default(),
|
|
}
|
|
}
|
|
|
|
pub fn encode(&mut self) -> Result<(), Error> {
|
|
loop {
|
|
let mut ibuf = [0; 3];
|
|
let mut obuf = [self.alphabet.pad(); 4];
|
|
let mut n_bytes = 0;
|
|
let mut num: u64 = 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;
|
|
}
|
|
if n_bytes == 0 {
|
|
break;
|
|
}
|
|
for (idx, n) in ibuf.iter().enumerate() {
|
|
num <<= 8;
|
|
if idx < n_bytes {
|
|
num |= *n as u64;
|
|
}
|
|
}
|
|
let mut outlen = n_bytes * 8 / 6;
|
|
if n_bytes * 8 % 6 > 0 {
|
|
outlen += 1;
|
|
}
|
|
for idx in (0..4).rev() {
|
|
if outlen == 4 || idx < outlen {
|
|
let b = num & 0b111111;
|
|
obuf[idx] = self.alphabet.items[b as usize];
|
|
}
|
|
num >>= 6;
|
|
}
|
|
write!(
|
|
self.writer,
|
|
"{}{}{}{}",
|
|
char::try_from(obuf[0]).unwrap(),
|
|
char::try_from(obuf[1]).unwrap(),
|
|
char::try_from(obuf[2]).unwrap(),
|
|
char::try_from(obuf[3]).unwrap()
|
|
)?;
|
|
if outlen < 4 {
|
|
break;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<R: Read> Encoder<R, String> {
|
|
pub fn output(self) -> String {
|
|
self.writer
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn encode() {
|
|
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=");
|
|
}
|
|
}
|