From 08dbe1c3fc531e145f253d4b7c8cbe48cfdc2143 Mon Sep 17 00:00:00 2001 From: Mathijs van de Nes Date: Wed, 10 Sep 2014 22:34:30 +0200 Subject: [PATCH] Starting implementation of a Writer --- src/bin/cksummer.rs | 13 ++-- src/crc32.rs | 29 ++------- src/lib.rs | 1 + src/reader.rs | 4 +- src/spec.rs | 13 ++++ src/util.rs | 5 ++ src/writer.rs | 144 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 176 insertions(+), 33 deletions(-) create mode 100644 src/writer.rs diff --git a/src/bin/cksummer.rs b/src/bin/cksummer.rs index 37f63712..dcc02ae4 100644 --- a/src/bin/cksummer.rs +++ b/src/bin/cksummer.rs @@ -2,19 +2,18 @@ extern crate zip; fn main() { - let stdin = std::io::stdin(); - let mut crc_reader = zip::crc32::Crc32Reader::new(stdin); - + let mut stdin = std::io::stdin(); + let mut crc = 0u32; let mut buf = [0u8, ..4096]; + loop { - match crc_reader.read(&mut buf) + match stdin.read(&mut buf) { Err(_) => break, - _ => {}, + Ok(n) => { crc = zip::crc32::crc32(crc, buf.slice_to(n)); }, } } - crc_reader.read_to_end().unwrap(); - println!("{:x}", crc_reader.get_checksum()); + println!("{:x}", crc); } diff --git a/src/crc32.rs b/src/crc32.rs index 636dec6c..67877c69 100644 --- a/src/crc32.rs +++ b/src/crc32.rs @@ -46,7 +46,7 @@ static CRC32_TABLE : [u32, ..256] = [ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d ]; -fn crc32(original: u32, buf: &[u8]) -> u32 +pub fn crc32(original: u32, buf: &[u8]) -> u32 { let mut crc = original ^ !0u32; @@ -62,43 +62,24 @@ pub struct Crc32Reader { inner: R, crc: u32, - check: Option, + check: u32, } impl Crc32Reader { - pub fn new(inner: R) -> Crc32Reader + pub fn new(inner: R, checksum: u32) -> Crc32Reader { Crc32Reader { inner: inner, crc: 0, - check: None, + check: checksum, } } - pub fn new_with_check(inner: R, checksum: u32) -> Crc32Reader - { - Crc32Reader - { - inner: inner, - crc: 0, - check: Some(checksum), - } - } - - pub fn get_checksum(&self) -> u32 - { - self.crc - } - fn check_matches(&self) -> bool { - match self.check - { - None => true, - Some(check) => check == self.crc - } + self.check == self.crc } } diff --git a/src/lib.rs b/src/lib.rs index a5b1872f..94559653 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,3 +9,4 @@ mod spec; pub mod crc32; pub mod reader; pub mod types; +pub mod writer; diff --git a/src/reader.rs b/src/reader.rs index 9e413728..b7203ba4 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -78,7 +78,7 @@ impl ZipReader types::Stored => { box - Crc32Reader::new_with_check( + Crc32Reader::new( limit_reader, file.crc32) as Box @@ -87,7 +87,7 @@ impl ZipReader { let deflate_reader = limit_reader.deflate_decode(); box - Crc32Reader::new_with_check( + Crc32Reader::new( deflate_reader, file.crc32) as Box diff --git a/src/spec.rs b/src/spec.rs index 5c943c84..622641d7 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -82,6 +82,19 @@ pub fn central_header_to_zip_file(reader: &mut R) -> IoResult(writer: &mut T, file: &ZipFile) -> IoResult<()> +{ + try!(writer.write_le_u32(LOCAL_FILE_HEADER_SIGNATURE)); + try!(writer.write_le_u16(20)); + let flag = if file.encrypted { 1 } else { 0 }; + try!(writer.write_le_u16(flag)); + try!(writer.write_le_u16(file.compression_method as u16)); + try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time))); + // ... + + Ok(()) +} + pub struct CentralDirectoryEnd { pub number_of_disks: u16, diff --git a/src/util.rs b/src/util.rs index b35c559d..efebf86b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -27,6 +27,11 @@ pub fn msdos_datetime_to_tm(time: u16, date: u16) -> Tm } } +pub fn tm_to_msdos_time(time: Tm) -> u16 +{ + ((time.tm_sec >> 2) | (time.tm_min << 5) | (time.tm_hour << 11)) as u16 +} + pub struct RefMutReader<'a, R:'a> { inner: RefMut<'a, R>, diff --git a/src/writer.rs b/src/writer.rs new file mode 100644 index 00000000..03888e04 --- /dev/null +++ b/src/writer.rs @@ -0,0 +1,144 @@ +use types; +use types::ZipFile; +use spec; +use std::default::Default; +use std::io; +use std::io::{IoResult, IoError}; +use std::mem; +use time; +use flate2; +use flate2::FlateWriter; +use flate2::writer::DeflateEncoder; + +enum GenericZipWriter +{ + Invalid, + Storer(W), + Deflater(DeflateEncoder), +} + +pub struct ZipWriter +{ + inner: GenericZipWriter, + files: Vec, + stats: ZipWriterStats, +} + +#[deriving(Default)] +struct ZipWriterStats +{ + crc32: u32, + start: u64, + bytes_written: u64, +} + +impl Writer for ZipWriter +{ + fn write(&mut self, buf: &[u8]) -> IoResult<()> + { + if self.files.len() == 0 { return Err(IoError { kind: io::OtherIoError, desc: "No file has been started", detail: None, }) } + self.stats.update(buf); + match self.inner + { + Storer(ref mut w) => w.write(buf), + Deflater(ref mut w) => w.write(buf), + Invalid => Err(IoError { kind: io::OtherIoError, desc: "The writer has previously failed", detail: None }), + } + } +} + +impl ZipWriterStats +{ + fn update(&mut self, buf: &[u8]) + { + self.crc32 = ::crc32::crc32(self.crc32, buf); + self.bytes_written += buf.len() as u64; + } +} + +impl ZipWriter +{ + pub fn new(inner: W) -> ZipWriter + { + ZipWriter + { + inner: Storer(inner), + files: Vec::new(), + stats: Default::default(), + } + } + + pub fn start_new_file(&mut self, name: &[u8], compression: types::CompressionMethod) -> IoResult<()> + { + try!(self.finish_file()); + + let writer = match self.inner + { + Storer(ref mut r) => r, + _ => fail!("Should have switched to stored first"), + }; + let header_start = try!(writer.tell()); + try!(writer.seek(30 + name.len() as i64, io::SeekCur)); + self.stats.start = header_start + 30 + name.len() as u64; + + self.files.push(ZipFile + { + encrypted: false, + compression_method: compression, + last_modified_time: time::now(), + crc32: 0, + compressed_size: 0, + uncompressed_size: 0, + file_name: Vec::from_slice(name), + file_comment: Vec::new(), + data_start: self.stats.start, + }); + + Ok(()) + } + + fn finish_file(&mut self) -> IoResult<()> + { + try!(self.inner.switch_to(types::Stored)); + let writer = match self.inner + { + Storer(ref mut r) => r, + _ => fail!("Should have switched to stored first"), + }; + + let file = match self.files.mut_last() + { + None => return Ok(()), + Some(f) => f, + }; + file.crc32 = self.stats.crc32; + file.uncompressed_size = self.stats.bytes_written; + file.compressed_size = try!(writer.tell()) - self.stats.start; + + // write header + try!(spec::write_local_file_header(writer, file)); + Ok(()) + } +} + +impl GenericZipWriter +{ + fn switch_to(&mut self, compression: types::CompressionMethod) -> IoResult<()> + { + let bare = match mem::replace(self, Invalid) + { + Storer(w) => w, + Deflater(w) => try!(w.finish()), + Invalid => return Err(IoError { kind: io::OtherIoError, desc: "The writer has previously failed", detail: None }), + }; + + *self = match compression + { + types::Stored => Storer(bare), + types::Deflated => Deflater(bare.deflate_encode(flate2::Default)), + _ => return Err(IoError { kind: io::OtherIoError, desc: "Unsupported compression requested", detail: None }), + }; + + Ok(()) + } +}