Add wrapping and styling. Todo: test styling, make sure there is no

double newline at the end of the output
This commit is contained in:
Nathan Fisher 2025-01-19 19:04:18 -05:00
parent 68138ab850
commit 29172fb5f9
5 changed files with 149 additions and 18 deletions

View file

@ -43,13 +43,13 @@ impl<R: Read, W: Write> Decoder<R, W> {
_ => unreachable!(),
}
let mut c = char::from(in_buf[0]);
if let Some(idx) = super::get_idx(c.to_ascii_uppercase()) {
if let Some(idx) = super::get_idx(c.to_ascii_uppercase().try_into().unwrap()) {
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()) {
if let Some(idx) = super::get_idx(c.to_ascii_uppercase().try_into().unwrap()) {
out_buf[0] |= idx as u8;
} else {
return Err(Error::IllegalChar(c));

View file

@ -3,25 +3,45 @@ use {
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) -> Self {
Self { reader, writer }
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 = [' '; 2];
let mut obuf = [b' '; 2];
let mut n_bytes = 0;
loop {
n_bytes += match self.reader.read(&mut ibuf) {
@ -32,13 +52,46 @@ impl<R: Read, W: Write> Encoder<R, W> {
break;
}
if n_bytes == 0 {
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];
write!(self.writer, "{}{}", obuf[0], obuf[1])?;
match self.style {
Style::Plain => {
if let Some(w) = self.wrap {
if total > 0 && total % w < 2 {
writeln!(self.writer)?;
}
}
self.writer.write_all(&obuf)?;
total += 2;
}
Style::Spaces => {
if let Some(w) = self.wrap {
if total % w < 3 {
writeln!(self.writer)?;
} else if total > 0 {
write!(self.writer, " ")?;
}
}
self.writer.write_all(&obuf)?;
total += 3;
}
Style::SpacesWithHex => {
if let Some(w) = self.wrap {
if total % w < 5 {
writeln!(self.writer)?;
} else if total > 0 {
write!(self.writer, " ")?;
}
}
write!(self.writer, "0x{}{}", obuf[0], obuf[1])?;
total += 5;
}
}
}
Ok(())
}
@ -51,20 +104,28 @@ impl<R: Read, W: Write> Encoder<R, W> {
#[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![]);
let mut encoder = Encoder::new("Hello, World".as_bytes(), vec![], None, None);
encoder.encode().unwrap();
assert_eq!(encoder.output(), b"48656C6C6F2C20576F726C64");
encoder = Encoder {
reader: "Hello, World!".as_bytes(),
writer: vec![],
};
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");
encoder = Encoder::new("Hello, World!\n".as_bytes(), vec![]);
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(76));
encoder.encode().unwrap();
assert_eq!(encoder.output(), b"48656C6C6F2C20576F726C64210A");
assert_eq!(encoder.writer, outfile.as_bytes());
}
}

View file

@ -4,11 +4,11 @@ mod encode;
mod error;
pub use {decode::Decoder, encode::Encoder, error::Error};
pub static B16_ALPHABET: [char; 16] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
pub static B16_ALPHABET: [u8; 16] = [
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', b'F',
];
pub(crate) fn get_idx(c: char) -> Option<usize> {
pub(crate) fn get_idx(c: u8) -> Option<usize> {
for (idx, x) in B16_ALPHABET.iter().enumerate() {
if *x == c {
return Some(idx);

9
testdata/lorem.txt vendored Normal file
View file

@ -0,0 +1,9 @@
Lorem ipsum odor amet, consectetuer adipiscing elit. Hendrerit eu semper lectus amet est proin. Tempus himenaeos at fusce lacus habitasse sociosqu torquent varius arcu. Odio netus fermentum nisl natoque per cubilia himenaeos urna. Elit venenatis eros risus vulputate duis interdum. Mauris dis ad pharetra dui aptent. Imperdiet orci auctor condimentum adipiscing arcu. Nullam tempor magna odio volutpat nisi. Condimentum scelerisque risus faucibus; duis sollicitudin nullam.
Condimentum ex fringilla tempus ultricies consectetur odio egestas venenatis vulputate. Sem sapien pellentesque ornare; viverra scelerisque consequat. Platea posuere mi senectus felis dolor dictumst mattis ultricies est. Malesuada elit adipiscing suspendisse est a. Justo luctus turpis tempus leo finibus rutrum nostra magnis aliquam. Felis posuere molestie praesent porta justo nisi. Etiam aptent conubia consectetur dis; penatibus elementum ut.
Morbi nostra netus ad nunc egestas neque nascetur fusce. Consequat ultrices scelerisque ut rhoncus volutpat, hendrerit convallis massa. Tortor himenaeos nullam malesuada suspendisse consectetur potenti volutpat. In suscipit sit maximus; lacinia eu nulla porta. Tempus commodo libero auctor cras bibendum sit. Metus aptent mauris nascetur venenatis diam metus. Inceptos mi ultrices class morbi donec. Orci pulvinar risus accumsan faucibus litora duis commodo commodo.
Aliquam etiam habitasse fames massa, hendrerit purus interdum nullam. Curabitur placerat tortor placerat nec adipiscing habitasse. Massa litora interdum pretium fusce nascetur primis faucibus magna. Dui curabitur molestie justo ullamcorper leo molestie mauris. Inceptos malesuada taciti litora libero maecenas neque nam laoreet. Tristique vivamus eget maecenas; porta fames curae mollis cras. Ipsum vulputate ullamcorper et nascetur nascetur vel orci.
Curae et odio; urna eros pulvinar malesuada eget dignissim. Cursus fermentum lectus phasellus tempor posuere vulputate quam. Mauris commodo commodo mauris inceptos metus varius urna sagittis. Ligula duis erat ipsum pretium eu facilisis. Duis lobortis proin facilisi ad suscipit vestibulum hac tortor interdum. Ultrices tincidunt maximus aptent phasellus in ullamcorper nisl varius dis. Venenatis enim potenti potenti sodales massa id elementum.

61
testdata/lorem_b16.txt vendored Normal file
View file

@ -0,0 +1,61 @@
4C6F72656D20697073756D206F646F7220616D65742C20636F6E736563746574756572206164
6970697363696E6720656C69742E2048656E6472657269742065752073656D706572206C6563
74757320616D6574206573742070726F696E2E2054656D7075732068696D656E61656F732061
74206675736365206C616375732068616269746173736520736F63696F73717520746F727175
656E742076617269757320617263752E204F64696F206E65747573206665726D656E74756D20
6E69736C206E61746F7175652070657220637562696C69612068696D656E61656F732075726E
612E20456C69742076656E656E617469732065726F732072697375732076756C707574617465
206475697320696E74657264756D2E204D617572697320646973206164207068617265747261
2064756920617074656E742E20496D70657264696574206F72636920617563746F7220636F6E
64696D656E74756D2061646970697363696E6720617263752E204E756C6C616D2074656D706F
72206D61676E61206F64696F20766F6C7574706174206E6973692E20436F6E64696D656E7475
6D207363656C657269737175652072697375732066617563696275733B206475697320736F6C
6C696369747564696E206E756C6C616D2E0A0A436F6E64696D656E74756D206578206672696E
67696C6C612074656D70757320756C7472696369657320636F6E7365637465747572206F6469
6F20656765737461732076656E656E617469732076756C7075746174652E2053656D20736170
69656E2070656C6C656E746573717565206F726E6172653B2076697665727261207363656C65
72697371756520636F6E7365717561742E20506C6174656120706F7375657265206D69207365
6E65637475732066656C697320646F6C6F722064696374756D7374206D617474697320756C74
726963696573206573742E204D616C65737561646120656C69742061646970697363696E6720
73757370656E64697373652065737420612E204A7573746F206C756374757320747572706973
2074656D707573206C656F2066696E696275732072757472756D206E6F73747261206D61676E
697320616C697175616D2E2046656C697320706F7375657265206D6F6C657374696520707261
6573656E7420706F727461206A7573746F206E6973692E20457469616D20617074656E742063
6F6E7562696120636F6E7365637465747572206469733B2070656E61746962757320656C656D
656E74756D2075742E0A0A4D6F726269206E6F73747261206E65747573206164206E756E6320
65676573746173206E65717565206E617363657475722066757363652E20436F6E7365717561
7420756C747269636573207363656C657269737175652075742072686F6E63757320766F6C75
747061742C2068656E64726572697420636F6E76616C6C6973206D617373612E20546F72746F
722068696D656E61656F73206E756C6C616D206D616C6573756164612073757370656E646973
736520636F6E736563746574757220706F74656E746920766F6C75747061742E20496E207375
73636970697420736974206D6178696D75733B206C6163696E6961206575206E756C6C612070
6F7274612E2054656D70757320636F6D6D6F646F206C696265726F20617563746F7220637261
7320626962656E64756D207369742E204D6574757320617074656E74206D6175726973206E61
7363657475722076656E656E61746973206469616D206D657475732E20496E636570746F7320
6D6920756C74726963657320636C617373206D6F72626920646F6E65632E204F726369207075
6C76696E617220726973757320616363756D73616E206661756369627573206C69746F726120
6475697320636F6D6D6F646F20636F6D6D6F646F2E0A0A416C697175616D20657469616D2068
61626974617373652066616D6573206D617373612C2068656E64726572697420707572757320
696E74657264756D206E756C6C616D2E2043757261626974757220706C61636572617420746F
72746F7220706C616365726174206E65632061646970697363696E6720686162697461737365
2E204D61737361206C69746F726120696E74657264756D207072657469756D20667573636520
6E61736365747572207072696D6973206661756369627573206D61676E612E20447569206375
72616269747572206D6F6C6573746965206A7573746F20756C6C616D636F72706572206C656F
206D6F6C6573746965206D61757269732E20496E636570746F73206D616C6573756164612074
6163697469206C69746F7261206C696265726F206D616563656E6173206E65717565206E616D
206C616F726565742E2054726973746971756520766976616D75732065676574206D61656365
6E61733B20706F7274612066616D6573206375726165206D6F6C6C697320637261732E204970
73756D2076756C70757461746520756C6C616D636F72706572206574206E6173636574757220
6E617363657475722076656C206F7263692E0A0A4375726165206574206F64696F3B2075726E
612065726F732070756C76696E6172206D616C6573756164612065676574206469676E697373
696D2E20437572737573206665726D656E74756D206C65637475732070686173656C6C757320
74656D706F7220706F73756572652076756C707574617465207175616D2E204D617572697320
636F6D6D6F646F20636F6D6D6F646F206D617572697320696E636570746F73206D6574757320
7661726975732075726E612073616769747469732E204C6967756C6120647569732065726174
20697073756D207072657469756D20657520666163696C697369732E2044756973206C6F626F
727469732070726F696E20666163696C69736920616420737573636970697420766573746962
756C756D2068616320746F72746F7220696E74657264756D2E20556C7472696365732074696E
636964756E74206D6178696D757320617074656E742070686173656C6C757320696E20756C6C
616D636F72706572206E69736C20766172697573206469732E2056656E656E6174697320656E
696D20706F74656E746920706F74656E746920736F64616C6573206D6173736120696420656C
656D656E74756D2E0A