135 lines
4 KiB
Rust
135 lines
4 KiB
Rust
use {
|
|
crate::{Error, B16_ALPHABET},
|
|
std::io::{ErrorKind, Read, Write},
|
|
};
|
|
|
|
pub enum Style {
|
|
Plain,
|
|
Spaces,
|
|
SpacesWithHex,
|
|
}
|
|
|
|
impl Default for Style {
|
|
fn default() -> Self {
|
|
Self::Plain
|
|
}
|
|
}
|
|
|
|
pub struct Encoder<R: Read, W: Write> {
|
|
reader: R,
|
|
writer: W,
|
|
style: Style,
|
|
wrap: Option<usize>,
|
|
}
|
|
|
|
impl<R: Read, W: Write> Encoder<R, W> {
|
|
/// Creates a new encoder with the given reader and writer. If alphabet is
|
|
/// `None` the encoder will use the default rfc4648 alphabet.
|
|
pub fn new(reader: R, writer: W, style: Option<Style>, wrap: Option<usize>) -> Self {
|
|
Self {
|
|
reader,
|
|
writer,
|
|
style: style.unwrap_or_default(),
|
|
wrap,
|
|
}
|
|
}
|
|
|
|
/// Encodes the given data as hexadecimal
|
|
/// # Errors
|
|
/// May return an error on IO failure
|
|
pub fn encode(&mut self) -> Result<(), Error> {
|
|
let mut total = 0;
|
|
loop {
|
|
let mut ibuf = [0; 1];
|
|
let mut obuf = [b' '; 2];
|
|
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;
|
|
}
|
|
if n_bytes == 0 {
|
|
if let Some(w) = self.wrap {
|
|
if total % w != 0 {
|
|
writeln!(self.writer)?;
|
|
}
|
|
} else {
|
|
writeln!(self.writer)?;
|
|
}
|
|
break;
|
|
}
|
|
let mut idx = usize::from(ibuf[0] & 0b1111);
|
|
obuf[1] = B16_ALPHABET[idx];
|
|
idx = usize::from((ibuf[0] & 0b1111_0000) >> 4);
|
|
obuf[0] = B16_ALPHABET[idx];
|
|
match self.style {
|
|
Style::Plain => {
|
|
if let Some(w) = self.wrap {
|
|
if total > 0 && total % w == 0 {
|
|
writeln!(self.writer)?;
|
|
}
|
|
}
|
|
self.writer.write_all(&obuf)?;
|
|
}
|
|
Style::Spaces => {
|
|
if let Some(w) = self.wrap {
|
|
if total > 0 && total % w == 0 {
|
|
writeln!(self.writer)?;
|
|
} else if total > 0 {
|
|
write!(self.writer, " ")?;
|
|
}
|
|
}
|
|
self.writer.write_all(&obuf)?;
|
|
}
|
|
Style::SpacesWithHex => {
|
|
if let Some(w) = self.wrap {
|
|
if total > 0 && total % w == 0 {
|
|
writeln!(self.writer)?;
|
|
} else if total > 0 {
|
|
write!(self.writer, " ")?;
|
|
}
|
|
}
|
|
write!(self.writer, "0x{}{}", obuf[0], obuf[1])?;
|
|
}
|
|
}
|
|
total += 1;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn output(self) -> W {
|
|
self.writer
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::{
|
|
fs::{self, File},
|
|
io::BufReader,
|
|
};
|
|
|
|
#[test]
|
|
fn encode() {
|
|
let mut encoder = Encoder::new("Hello, World".as_bytes(), vec![], None, None);
|
|
encoder.encode().unwrap();
|
|
assert_eq!(encoder.output(), b"48656C6C6F2C20576F726C64\n");
|
|
encoder = Encoder::new("Hello, World!".as_bytes(), vec![], None, None);
|
|
encoder.encode().unwrap();
|
|
assert_eq!(encoder.output(), b"48656C6C6F2C20576F726C6421\n");
|
|
}
|
|
|
|
#[test]
|
|
fn encode_wrapping() {
|
|
let infile = File::open("testdata/lorem.txt").unwrap();
|
|
let reader = BufReader::new(infile);
|
|
let outfile = fs::read_to_string("testdata/lorem_b16.txt").unwrap();
|
|
let mut encoder = Encoder::new(reader, vec![], None, Some(38));
|
|
encoder.encode().unwrap();
|
|
assert_eq!(encoder.writer, outfile.as_bytes());
|
|
}
|
|
}
|