2019-02-12 16:13:11 +01:00
#[ 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! (
2019-02-14 21:19:24 +01:00
" Error writing \" {} \" : \n {} " ,
2019-02-12 16:13:11 +01:00
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 = [ 0 u8 ; BUF_SIZE ] ; // also used for output
let mut in2_buffer = [ 0 u8 ; 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 )
}
}