Initial commit
This commit is contained in:
commit
3ab6634661
5 changed files with 194 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "b2"
|
||||
version = "0.1.0"
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "b2"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
176
src/encode.rs
Normal file
176
src/encode.rs
Normal file
|
@ -0,0 +1,176 @@
|
|||
use std::io::{self, ErrorKind, Read, Write};
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub enum Style {
|
||||
#[default]
|
||||
Plain,
|
||||
Spaces,
|
||||
Prefixed,
|
||||
Table,
|
||||
}
|
||||
|
||||
pub struct Encoder<R: Read, W: Write> {
|
||||
reader: R,
|
||||
writer: W,
|
||||
style: Style,
|
||||
wrap: Option<usize>,
|
||||
}
|
||||
|
||||
impl<R: Read, W: Write> Encoder<R, W> {
|
||||
pub fn new(reader: R, writer: W, style: Option<Style>, mut wrap: Option<usize>) -> Self {
|
||||
let style = style.unwrap_or_default();
|
||||
if let Style::Table = style {
|
||||
wrap = match wrap {
|
||||
Some(u) => Some(u),
|
||||
None => Some(5),
|
||||
};
|
||||
}
|
||||
Self {
|
||||
reader,
|
||||
writer,
|
||||
style,
|
||||
wrap,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(mut self) -> Result<W, io::Error> {
|
||||
let mut bytes = self.reader.bytes();
|
||||
let mut total = 0;
|
||||
loop {
|
||||
match bytes.next() {
|
||||
Some(Ok(b)) => match self.style {
|
||||
Style::Plain => {
|
||||
if let Some(w) = self.wrap {
|
||||
if total > 0 && total % w == 0 {
|
||||
writeln!(self.writer)?;
|
||||
}
|
||||
}
|
||||
write!(self.writer, "{b:08b}")?;
|
||||
}
|
||||
Style::Spaces => {
|
||||
if let Some(w) = self.wrap {
|
||||
if total > 0 && total % w == 0 {
|
||||
writeln!(self.writer)?;
|
||||
} else {
|
||||
write!(self.writer, " ")?;
|
||||
}
|
||||
} else if total > 0 {
|
||||
write!(self.writer, " ")?;
|
||||
}
|
||||
write!(self.writer, "{b:08b}")?;
|
||||
}
|
||||
Style::Prefixed => {
|
||||
if let Some(w) = self.wrap {
|
||||
if total > 0 && total % w == 0 {
|
||||
writeln!(self.writer)?;
|
||||
} else if total > 0 {
|
||||
write!(self.writer, " ")?;
|
||||
}
|
||||
} else if total > 0 {
|
||||
write!(self.writer, " ")?;
|
||||
}
|
||||
write!(self.writer, "{b:#010b}")?;
|
||||
}
|
||||
Style::Table => {
|
||||
if let Some(w) = self.wrap {
|
||||
if total % w == 0 {
|
||||
if total > 0 {
|
||||
write!(self.writer, "\n{total:>5}\t{b:#010b}")?;
|
||||
} else {
|
||||
write!(self.writer, "{total:>5}\t{b:#010b}")?;
|
||||
}
|
||||
} else {
|
||||
write!(self.writer, " {b:#010b}")?;
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(Err(e)) if e.kind() == ErrorKind::UnexpectedEof => break,
|
||||
Some(Err(e)) if e.kind() == ErrorKind::Interrupted => continue,
|
||||
Some(Err(e)) => return Err(e),
|
||||
None => break,
|
||||
}
|
||||
total += 1;
|
||||
}
|
||||
if let Some(w) = self.wrap {
|
||||
if total % w != 0 {
|
||||
writeln!(self.writer)?;
|
||||
}
|
||||
} else {
|
||||
writeln!(self.writer)?;
|
||||
}
|
||||
Ok(self.writer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn hello_plain() {
|
||||
let encoder = Encoder::new("Hello, World!".as_bytes(), vec![], None, None);
|
||||
assert_eq!(
|
||||
encoder.encode().unwrap(),
|
||||
b"01001000011001010110110001101100011011110010110000100000010101110110111101110010011011000110010000100001\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_spaced() {
|
||||
let encoder = Encoder::new(
|
||||
"Hello, World!".as_bytes(),
|
||||
vec![],
|
||||
Some(Style::Spaces),
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
encoder.encode().unwrap(),
|
||||
b"01001000 01100101 01101100 01101100 01101111 00101100 00100000 01010111 01101111 01110010 01101100 01100100 00100001\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_prefixed() {
|
||||
let encoder = Encoder::new(
|
||||
"Hello, World!".as_bytes(),
|
||||
vec![],
|
||||
Some(Style::Prefixed),
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
encoder.encode().unwrap(),
|
||||
b"0b01001000 0b01100101 0b01101100 0b01101100 0b01101111 0b00101100 0b00100000 0b01010111 0b01101111 0b01110010 0b01101100 0b01100100 0b00100001\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_prefixed_wrapped() {
|
||||
let encoder = Encoder::new(
|
||||
"Hello, World!".as_bytes(),
|
||||
vec![],
|
||||
Some(Style::Prefixed),
|
||||
Some(4),
|
||||
);
|
||||
assert_eq!(
|
||||
encoder.encode().unwrap(),
|
||||
b"0b01001000 0b01100101 0b01101100 0b01101100\n0b01101111 0b00101100 0b00100000 0b01010111\n0b01101111 0b01110010 0b01101100 0b01100100\n0b00100001\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_table() {
|
||||
let encoder = Encoder::new(
|
||||
"Hello, World!".as_bytes(),
|
||||
vec![],
|
||||
Some(Style::Table),
|
||||
Some(4),
|
||||
);
|
||||
assert_eq!(
|
||||
encoder.encode().unwrap(),
|
||||
b" 0\t0b01001000 0b01100101 0b01101100 0b01101100\n 4\t0b01101111 0b00101100 0b00100000 0b01010111\n 8\t0b01101111 0b01110010 0b01101100 0b01100100\n 12\t0b00100001\n"
|
||||
);
|
||||
}
|
||||
}
|
4
src/lib.rs
Normal file
4
src/lib.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
mod encode;
|
||||
pub use encode::{Encoder, Style};
|
Loading…
Add table
Reference in a new issue