Starting implementation of a Writer
This commit is contained in:
parent
7df03b54f9
commit
08dbe1c3fc
7 changed files with 176 additions and 33 deletions
|
@ -2,19 +2,18 @@ extern crate zip;
|
||||||
|
|
||||||
fn main()
|
fn main()
|
||||||
{
|
{
|
||||||
let stdin = std::io::stdin();
|
let mut stdin = std::io::stdin();
|
||||||
let mut crc_reader = zip::crc32::Crc32Reader::new(stdin);
|
let mut crc = 0u32;
|
||||||
|
|
||||||
let mut buf = [0u8, ..4096];
|
let mut buf = [0u8, ..4096];
|
||||||
|
|
||||||
loop
|
loop
|
||||||
{
|
{
|
||||||
match crc_reader.read(&mut buf)
|
match stdin.read(&mut buf)
|
||||||
{
|
{
|
||||||
Err(_) => break,
|
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);
|
||||||
}
|
}
|
||||||
|
|
29
src/crc32.rs
29
src/crc32.rs
|
@ -46,7 +46,7 @@ static CRC32_TABLE : [u32, ..256] = [
|
||||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||||
];
|
];
|
||||||
|
|
||||||
fn crc32(original: u32, buf: &[u8]) -> u32
|
pub fn crc32(original: u32, buf: &[u8]) -> u32
|
||||||
{
|
{
|
||||||
let mut crc = original ^ !0u32;
|
let mut crc = original ^ !0u32;
|
||||||
|
|
||||||
|
@ -62,43 +62,24 @@ pub struct Crc32Reader<R>
|
||||||
{
|
{
|
||||||
inner: R,
|
inner: R,
|
||||||
crc: u32,
|
crc: u32,
|
||||||
check: Option<u32>,
|
check: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Reader> Crc32Reader<R>
|
impl<R: Reader> Crc32Reader<R>
|
||||||
{
|
{
|
||||||
pub fn new(inner: R) -> Crc32Reader<R>
|
pub fn new(inner: R, checksum: u32) -> Crc32Reader<R>
|
||||||
{
|
{
|
||||||
Crc32Reader
|
Crc32Reader
|
||||||
{
|
{
|
||||||
inner: inner,
|
inner: inner,
|
||||||
crc: 0,
|
crc: 0,
|
||||||
check: None,
|
check: checksum,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_check(inner: R, checksum: u32) -> Crc32Reader<R>
|
|
||||||
{
|
|
||||||
Crc32Reader
|
|
||||||
{
|
|
||||||
inner: inner,
|
|
||||||
crc: 0,
|
|
||||||
check: Some(checksum),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_checksum(&self) -> u32
|
|
||||||
{
|
|
||||||
self.crc
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_matches(&self) -> bool
|
fn check_matches(&self) -> bool
|
||||||
{
|
{
|
||||||
match self.check
|
self.check == self.crc
|
||||||
{
|
|
||||||
None => true,
|
|
||||||
Some(check) => check == self.crc
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,3 +9,4 @@ mod spec;
|
||||||
pub mod crc32;
|
pub mod crc32;
|
||||||
pub mod reader;
|
pub mod reader;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
pub mod writer;
|
||||||
|
|
|
@ -78,7 +78,7 @@ impl<T: Reader+Seek> ZipReader<T>
|
||||||
types::Stored =>
|
types::Stored =>
|
||||||
{
|
{
|
||||||
box
|
box
|
||||||
Crc32Reader::new_with_check(
|
Crc32Reader::new(
|
||||||
limit_reader,
|
limit_reader,
|
||||||
file.crc32)
|
file.crc32)
|
||||||
as Box<Reader>
|
as Box<Reader>
|
||||||
|
@ -87,7 +87,7 @@ impl<T: Reader+Seek> ZipReader<T>
|
||||||
{
|
{
|
||||||
let deflate_reader = limit_reader.deflate_decode();
|
let deflate_reader = limit_reader.deflate_decode();
|
||||||
box
|
box
|
||||||
Crc32Reader::new_with_check(
|
Crc32Reader::new(
|
||||||
deflate_reader,
|
deflate_reader,
|
||||||
file.crc32)
|
file.crc32)
|
||||||
as Box<Reader>
|
as Box<Reader>
|
||||||
|
|
13
src/spec.rs
13
src/spec.rs
|
@ -82,6 +82,19 @@ pub fn central_header_to_zip_file<R: Reader+Seek>(reader: &mut R) -> IoResult<Zi
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_local_file_header<T: Writer>(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 struct CentralDirectoryEnd
|
||||||
{
|
{
|
||||||
pub number_of_disks: u16,
|
pub number_of_disks: u16,
|
||||||
|
|
|
@ -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>
|
pub struct RefMutReader<'a, R:'a>
|
||||||
{
|
{
|
||||||
inner: RefMut<'a, R>,
|
inner: RefMut<'a, R>,
|
||||||
|
|
144
src/writer.rs
Normal file
144
src/writer.rs
Normal file
|
@ -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<W>
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
Storer(W),
|
||||||
|
Deflater(DeflateEncoder<W>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ZipWriter<W>
|
||||||
|
{
|
||||||
|
inner: GenericZipWriter<W>,
|
||||||
|
files: Vec<ZipFile>,
|
||||||
|
stats: ZipWriterStats,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(Default)]
|
||||||
|
struct ZipWriterStats
|
||||||
|
{
|
||||||
|
crc32: u32,
|
||||||
|
start: u64,
|
||||||
|
bytes_written: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Writer+Seek> Writer for ZipWriter<W>
|
||||||
|
{
|
||||||
|
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<W: Writer+Seek> ZipWriter<W>
|
||||||
|
{
|
||||||
|
pub fn new(inner: W) -> ZipWriter<W>
|
||||||
|
{
|
||||||
|
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<W: Writer+Seek> GenericZipWriter<W>
|
||||||
|
{
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue