xor-files/src/worker/mod.rs

161 lines
4.3 KiB
Rust

#[cfg(test)]
mod operationtest;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
pub trait XORWorker {
fn work(input: [&str; 2], output: &str) -> Result<(), String>;
}
pub struct Worker;
fn open(name: &str, writable: bool) -> Result<File, String> {
let f = OpenOptions::new()
.read(!writable)
.write(writable)
.create(writable)
.open(name);
match f {
Ok(file) => Ok(file),
Err(e) => Err(format!(
"Could not open file \"{}\" for {}:\n{}",
name,
if writable {"writing"} else {"reading"},
e
)),
}
}
const BUF_SIZE: usize = (1 << 16);
struct ReadWrapper<'a, T>
where T: Read {
stream: T,
name: &'a str,
}
impl<'a, T> ReadWrapper<'a, T>
where T: Read {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, String> {
match self.stream.read(buf) {
Ok(size) => Ok(size),
Err(e) => Err(format!(
"Error reading \"{}\":\n{}",
self.name,
e
)),
}
}
}
struct WriteWrapper<'a, T>
where T: Write {
stream: T,
name: &'a str,
}
impl<'a, T> WriteWrapper<'a, T>
where T: Write {
fn write_all(&mut self, buf: &[u8]) -> Result<(), String> {
match self.stream.write_all(buf) {
Ok(()) => Ok(()),
Err(e) => Err(format!(
"Error writing \"{}\":\n{}",
self.name,
e
)),
}
}
}
fn xor_into(dest: &mut [u8], src: &[u8]) {
for i in 0..dest.len() {
dest[i] ^= src[i];
}
}
fn operate<I1, I2, O>(
mut in1: ReadWrapper<I1>,
mut in2: ReadWrapper<I2>,
mut out: WriteWrapper<O>
) -> Result<(), String>
where I1: Read,
I2: Read,
O: Write
{
let mut in1_buffer = [0u8; BUF_SIZE]; // also used for output
let mut in2_buffer = [0u8; BUF_SIZE];
let mut in1_remaining = &mut[][..]; // empty at begin
let mut in2_remaining = &[][..];
let mut in2_finished = false;
loop {
let l1 = in1_remaining.len();
let l2 = in2_remaining.len();
match (in2_finished, l1, l2) {
(_, 0, _) => {
// in1 buffer is empty, read
match in1.read(&mut in1_buffer[..])? {
0 => {
// nothing more on input1, finished
return Ok(());
},
size => {
// take data
in1_remaining = &mut in1_buffer[0..size];
},
};
},
(false, other, 0) => {
// in2 buffer is empty, read
let toread = if other == 0 {BUF_SIZE} else {other};
in2_remaining = &[][..];
match in2.read(&mut in2_buffer[0..toread])? {
0 => {
// nothing more on input2
in2_finished = true;
},
size => {
in2_remaining = &in2_buffer[0..size];
},
};
},
(true, _, 0) => {
// Data from i1 must be streamed to output
out.write_all(&in1_remaining)?;
in1_remaining = &mut in1_remaining[0..0];
},
(_, i1size, i2size) => {
// There is data on both sides, process it
let amount = if i1size < i2size {i1size} else {i2size};
xor_into(&mut in1_remaining[0..amount], &in2_remaining[0..amount]);
out.write_all(&in1_remaining[0..amount])?;
in1_remaining = &mut in1_remaining[amount..];
in2_remaining = &in2_remaining[amount..];
},
};
}
}
impl XORWorker for Worker {
fn work(input: [&str; 2], output: &str) -> Result<(), String> {
let in1 = ReadWrapper {
stream: open(input[0], false)?,
name: input[0],
};
let in2 = ReadWrapper {
stream: open(input[1], false)?,
name: input[1],
};
let out = WriteWrapper {
stream: open(output, true)?,
name: output,
};
operate(in1, in2, out)
}
}