#[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 { 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 { 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( mut in1: ReadWrapper, mut in2: ReadWrapper, mut out: WriteWrapper ) -> 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) } }