use crate::binarytext::BinaryText;
use crate::error::BinTxtError;
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Stdout, Write, stdout};
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Mode {
Decode,
Encode,
}
pub fn buf_reader_file(filename: &str) -> Result<BufReader<File>, BinTxtError> {
let f = match File::open(filename) {
Ok(file) => file,
Err(_) => {
let msg = format!("Error opening file {} for reading", filename);
return Err(BinTxtError::IOError(msg));
}
};
Ok(BufReader::new(f))
}
pub fn buf_reader_bytes(bytes: &[u8]) -> Result<BufReader<&[u8]>, BinTxtError> {
Ok(BufReader::new(bytes))
}
pub fn buf_writer_stdout() -> Result<BufWriter<Stdout>, BinTxtError> {
Ok(BufWriter::new(stdout()))
}
pub fn buf_writer_file(filename: &str) -> Result<BufWriter<File>, BinTxtError> {
let f = match File::create(filename) {
Ok(file) => file,
Err(_) => {
let msg = format!("Error opening file {} for writing", filename);
return Err(BinTxtError::IOError(msg));
}
};
Ok(BufWriter::new(f))
}
pub fn buf_writer_bytes() -> Result<BufWriter<Vec<u8>>, BinTxtError> {
Ok(BufWriter::new(Vec::<u8>::new()))
}
pub struct BinTxtStream<'a, R: Read, W: Write, B: BinaryText> {
buf_reader: BufReader<R>,
buf_writer: BufWriter<W>,
bintxt: &'a B,
mode: Mode,
buf_len: usize,
width: usize,
}
impl<'a, R: Read, W: Write, B: BinaryText> BinTxtStream<'a, R, W, B> {
pub fn new(
buf_reader: BufReader<R>,
buf_writer: BufWriter<W>,
bintxt: &'a B,
mode: Mode,
buf_len: usize,
width: usize,
) -> Self {
Self {
buf_reader,
buf_writer,
bintxt,
mode,
buf_len,
width,
}
}
pub fn source_ref(&self) -> &R {
self.buf_reader.get_ref()
}
pub fn dest_ref(&self) -> &W {
self.buf_writer.get_ref()
}
fn buffer_mod(buf_len: usize, div: usize) -> Vec<u8> {
if buf_len == 0 || !buf_len.is_multiple_of(div) {
let add = div - buf_len % div;
vec![0; buf_len + add]
} else {
vec![0; buf_len]
}
}
fn filter_buffer_dec(buffer: &mut [u8]) -> usize {
let len_orig = buffer.len();
let mut ind = 0;
for i in 0..len_orig {
let val = buffer[i];
if val >= 0x20 {
if ind < i {
buffer[ind] = val;
}
ind += 1;
}
}
ind
}
fn read_into_buffer(&mut self, buffer: &mut [u8]) -> Result<usize, BinTxtError> {
let mut offset = 0;
let len = buffer.len();
loop {
match self.buf_reader.read(&mut buffer[offset..len]) {
Ok(n_bytes_read) => {
let end = match self.mode {
Mode::Decode => {
let e = offset + n_bytes_read;
let len_new = Self::filter_buffer_dec(&mut buffer[offset..e]);
offset + len_new
}
Mode::Encode => offset + n_bytes_read,
};
if n_bytes_read == 0 || end == len {
return Ok(end);
}
offset = end;
}
Err(_) => {
let msg = "Error reading buffer";
return Err(BinTxtError::IOError(msg.to_string()));
}
}
}
}
fn write_buffer(&mut self, buffer: &[u8]) -> Result<(), BinTxtError> {
match self.buf_writer.write_all(buffer) {
Ok(()) => {}
Err(_) => {
let msg = "Error writing buffer";
return Err(BinTxtError::IOError(msg.to_string()));
}
}
Ok(())
}
fn write_buffer_with_newlines(
&mut self,
buffer: &[u8],
pos_next_newline: usize,
) -> Result<usize, BinTxtError> {
if self.width > 0 {
self.write_buffer(&buffer[0..pos_next_newline])?;
self.write_buffer(b"\n")?;
let iter_chunks = buffer[pos_next_newline..].chunks_exact(self.width);
let rem = iter_chunks.remainder();
for c in iter_chunks {
self.write_buffer(c)?;
self.write_buffer(b"\n")?;
}
if !rem.is_empty() {
self.write_buffer(rem)?;
}
return Ok(self.width - rem.len());
} else {
self.write_buffer(buffer)?;
}
Ok(0)
}
pub fn stream(&mut self) -> Result<(), BinTxtError> {
let (n_bytes_encode, n_bytes_decode) =
(self.bintxt.n_bytes_encode(), self.bintxt.n_bytes_decode());
let (align, mul) = match self.mode {
Mode::Encode => (n_bytes_encode, n_bytes_encode / n_bytes_decode),
Mode::Decode => (n_bytes_decode, n_bytes_encode / n_bytes_decode),
};
let mut buffer_read = Self::buffer_mod(self.buf_len, align);
let mut buffer_write = Vec::<u8>::with_capacity(self.buf_len * mul);
let mut pos_next_newline = self.width;
loop {
let n_bytes_read = self.read_into_buffer(&mut buffer_read)?;
if n_bytes_read == 0 {
break;
}
match self.mode {
Mode::Encode => {
self.bintxt
.encode_into_vec(&buffer_read[0..n_bytes_read], &mut buffer_write)?;
}
Mode::Decode => {
self.bintxt
.decode_into_vec(&buffer_read[0..n_bytes_read], &mut buffer_write)?;
}
}
pos_next_newline =
self.write_buffer_with_newlines(buffer_write.as_slice(), pos_next_newline)?;
}
match self.buf_writer.flush() {
Ok(_) => Ok(()),
Err(_) => {
let msg = "Error flushing write buffer".to_string();
Err(BinTxtError::IOError(msg))
}
}
}
}
#[cfg(test)]
mod tests {
use crate::base64::Base64;
use crate::stream::{BinTxtStream, Mode};
use std::io::{BufReader, BufWriter};
#[test]
fn test_stream() {
let source =
"gr3n8t94h89hf849h29ht894h2989n2t8928tn493h8th843h08tj493jt9403jt04j3089jz0649hh89"
.as_bytes();
let res = "Z3Izbjh0OTRoO\nDloZjg0OWgyOW\nh0ODk0aDI5ODl\nuMnQ4OTI4dG40\nOTNoOHRoODQza\nDA4dGo0OTNqdD\nk0MDNqdDA0ajM\nwODlqejA2NDlo\naDg5".as_bytes();
let bintxt = Base64::new();
let buf_reader = BufReader::new(source);
let test = Vec::<u8>::with_capacity(res.len());
let buf_writer = BufWriter::new(test);
let mut stream = BinTxtStream::new(buf_reader, buf_writer, &bintxt, Mode::Encode, 24, 13);
stream.stream().unwrap();
let test = stream.dest_ref();
assert_eq!(res, test);
}
#[test]
fn test_filter_dec() {
let mut buffer = "grne9unt95j9j89gr39j9\nf78h784hr\n0950490\ngbzu8h"
.as_bytes()
.iter()
.cloned()
.collect::<Vec<u8>>();
let buffer_test = "grne9unt95j9j89gr39j9f78h784hr0950490gbzu8h".as_bytes();
let length_new =
BinTxtStream::<BufReader<&[u8]>, BufWriter<&mut [u8]>, Base64>::filter_buffer_dec(
&mut buffer,
);
assert_eq!(buffer_test[0..], buffer[0..length_new]);
}
}