Use external crate for extra read/write methods

This commit is contained in:
Mathijs van de Nes 2015-03-09 11:55:19 +01:00
parent 785dccbf8e
commit aa4e395ec9
6 changed files with 76 additions and 153 deletions

View file

@ -13,3 +13,4 @@ Library to support the reading and writing of zip files.
flate2 = "^0.2" flate2 = "^0.2"
bzip2 = "^0.2" bzip2 = "^0.2"
time = "*" time = "*"
podio = "^0.0"

View file

@ -8,6 +8,7 @@
extern crate time; extern crate time;
extern crate flate2; extern crate flate2;
extern crate bzip2; extern crate bzip2;
extern crate podio;
pub use read::ZipArchive; pub use read::ZipArchive;
pub use write::ZipWriter; pub use write::ZipWriter;

View file

@ -12,7 +12,7 @@ use flate2;
use flate2::FlateReadExt; use flate2::FlateReadExt;
use bzip2::reader::BzDecompressor; use bzip2::reader::BzDecompressor;
use util; use util;
use util::ReadIntExt; use podio::{ReadPodExt, LittleEndian};
use types::ZipFileData; use types::ZipFileData;
/// Wrapper for reading the contents of a ZIP file. /// Wrapper for reading the contents of a ZIP file.
@ -171,30 +171,30 @@ impl<R: Read+io::Seek> ZipArchive<R>
fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R) -> ZipResult<ZipFileData> fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R) -> ZipResult<ZipFileData>
{ {
// Parse central header // Parse central header
let signature = try!(reader.read_le_u32()); let signature = try!(reader.read_u32::<LittleEndian>());
if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
{ {
return Err(ZipError::InvalidArchive("Invalid Central Directory header")) return Err(ZipError::InvalidArchive("Invalid Central Directory header"))
} }
try!(reader.read_le_u16()); try!(reader.read_u16::<LittleEndian>());
try!(reader.read_le_u16()); try!(reader.read_u16::<LittleEndian>());
let flags = try!(reader.read_le_u16()); let flags = try!(reader.read_u16::<LittleEndian>());
let encrypted = flags & 1 == 1; let encrypted = flags & 1 == 1;
let is_utf8 = flags & (1 << 11) != 0; let is_utf8 = flags & (1 << 11) != 0;
let compression_method = try!(reader.read_le_u16()); let compression_method = try!(reader.read_u16::<LittleEndian>());
let last_mod_time = try!(reader.read_le_u16()); let last_mod_time = try!(reader.read_u16::<LittleEndian>());
let last_mod_date = try!(reader.read_le_u16()); let last_mod_date = try!(reader.read_u16::<LittleEndian>());
let crc32 = try!(reader.read_le_u32()); let crc32 = try!(reader.read_u32::<LittleEndian>());
let compressed_size = try!(reader.read_le_u32()); let compressed_size = try!(reader.read_u32::<LittleEndian>());
let uncompressed_size = try!(reader.read_le_u32()); let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
let file_name_length = try!(reader.read_le_u16()) as usize; let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
let extra_field_length = try!(reader.read_le_u16()) as usize; let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
let file_comment_length = try!(reader.read_le_u16()) as usize; let file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
try!(reader.read_le_u16()); try!(reader.read_u16::<LittleEndian>());
try!(reader.read_le_u16()); try!(reader.read_u16::<LittleEndian>());
try!(reader.read_le_u32()); try!(reader.read_u32::<LittleEndian>());
let offset = try!(reader.read_le_u32()) as u64; let offset = try!(reader.read_u32::<LittleEndian>()) as u64;
let file_name_raw = try!(reader.read_exact(file_name_length)); let file_name_raw = try!(reader.read_exact(file_name_length));
let extra_field = try!(reader.read_exact(extra_field_length)); let extra_field = try!(reader.read_exact(extra_field_length));
let file_comment_raw = try!(reader.read_exact(file_comment_length)); let file_comment_raw = try!(reader.read_exact(file_comment_length));
@ -215,15 +215,15 @@ fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R) -> ZipResult<Zip
// Parse local header // Parse local header
try!(reader.seek(io::SeekFrom::Start(offset))); try!(reader.seek(io::SeekFrom::Start(offset)));
let signature = try!(reader.read_le_u32()); let signature = try!(reader.read_u32::<LittleEndian>());
if signature != spec::LOCAL_FILE_HEADER_SIGNATURE if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
{ {
return Err(ZipError::InvalidArchive("Invalid local file header")) return Err(ZipError::InvalidArchive("Invalid local file header"))
} }
try!(reader.seek(io::SeekFrom::Current(22))); try!(reader.seek(io::SeekFrom::Current(22)));
let file_name_length = try!(reader.read_le_u16()) as u64; let file_name_length = try!(reader.read_u16::<LittleEndian>()) as u64;
let extra_field_length = try!(reader.read_le_u16()) as u64; let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as u64;
let magic_and_header = 4 + 22 + 2 + 2; let magic_and_header = 4 + 22 + 2 + 2;
let data_start = offset as u64 + magic_and_header + file_name_length + extra_field_length; let data_start = offset as u64 + magic_and_header + file_name_length + extra_field_length;
@ -256,8 +256,8 @@ fn parse_extra_field(_file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
while (reader.position() as usize) < data.len() while (reader.position() as usize) < data.len()
{ {
let kind = try!(reader.read_le_u16()); let kind = try!(reader.read_u16::<LittleEndian>());
let len = try!(reader.read_le_u16()); let len = try!(reader.read_u16::<LittleEndian>());
match kind match kind
{ {
_ => try!(reader.seek(io::SeekFrom::Current(len as i64))), _ => try!(reader.seek(io::SeekFrom::Current(len as i64))),

View file

@ -2,7 +2,7 @@ use std::io;
use std::io::prelude::*; use std::io::prelude::*;
use result::{ZipResult, ZipError}; use result::{ZipResult, ZipError};
use std::iter::range_step_inclusive; use std::iter::range_step_inclusive;
use util::{ReadIntExt, WriteIntExt}; use podio::{ReadPodExt, WritePodExt, LittleEndian};
pub static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50; pub static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
pub static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50; pub static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
@ -23,18 +23,18 @@ impl CentralDirectoryEnd
{ {
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
{ {
let magic = try!(reader.read_le_u32()); let magic = try!(reader.read_u32::<LittleEndian>());
if magic != CENTRAL_DIRECTORY_END_SIGNATURE if magic != CENTRAL_DIRECTORY_END_SIGNATURE
{ {
return Err(ZipError::InvalidArchive("Invalid digital signature header")) return Err(ZipError::InvalidArchive("Invalid digital signature header"))
} }
let disk_number = try!(reader.read_le_u16()); let disk_number = try!(reader.read_u16::<LittleEndian>());
let disk_with_central_directory = try!(reader.read_le_u16()); let disk_with_central_directory = try!(reader.read_u16::<LittleEndian>());
let number_of_files_on_this_disk = try!(reader.read_le_u16()); let number_of_files_on_this_disk = try!(reader.read_u16::<LittleEndian>());
let number_of_files = try!(reader.read_le_u16()); let number_of_files = try!(reader.read_u16::<LittleEndian>());
let central_directory_size = try!(reader.read_le_u32()); let central_directory_size = try!(reader.read_u32::<LittleEndian>());
let central_directory_offset = try!(reader.read_le_u32()); let central_directory_offset = try!(reader.read_u32::<LittleEndian>());
let zip_file_comment_length = try!(reader.read_le_u16()) as usize; let zip_file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
let zip_file_comment = try!(reader.read_exact(zip_file_comment_length)); let zip_file_comment = try!(reader.read_exact(zip_file_comment_length));
Ok(CentralDirectoryEnd Ok(CentralDirectoryEnd
@ -59,10 +59,10 @@ impl CentralDirectoryEnd
for pos in range_step_inclusive(file_length - header_size, search_upper_bound, -1) for pos in range_step_inclusive(file_length - header_size, search_upper_bound, -1)
{ {
try!(reader.seek(io::SeekFrom::Start(pos as u64))); try!(reader.seek(io::SeekFrom::Start(pos as u64)));
if try!(reader.read_le_u32()) == CENTRAL_DIRECTORY_END_SIGNATURE if try!(reader.read_u32::<LittleEndian>()) == CENTRAL_DIRECTORY_END_SIGNATURE
{ {
try!(reader.seek(io::SeekFrom::Current(bytes_between_magic_and_comment_size))); try!(reader.seek(io::SeekFrom::Current(bytes_between_magic_and_comment_size)));
let comment_length = try!(reader.read_le_u16()) as i64; let comment_length = try!(reader.read_u16::<LittleEndian>()) as i64;
if file_length - pos - header_size == comment_length if file_length - pos - header_size == comment_length
{ {
try!(reader.seek(io::SeekFrom::Start(pos as u64))); try!(reader.seek(io::SeekFrom::Start(pos as u64)));
@ -75,14 +75,14 @@ impl CentralDirectoryEnd
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()>
{ {
try!(writer.write_le_u32(CENTRAL_DIRECTORY_END_SIGNATURE)); try!(writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE));
try!(writer.write_le_u16(self.disk_number)); try!(writer.write_u16::<LittleEndian>(self.disk_number));
try!(writer.write_le_u16(self.disk_with_central_directory)); try!(writer.write_u16::<LittleEndian>(self.disk_with_central_directory));
try!(writer.write_le_u16(self.number_of_files_on_this_disk)); try!(writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk));
try!(writer.write_le_u16(self.number_of_files)); try!(writer.write_u16::<LittleEndian>(self.number_of_files));
try!(writer.write_le_u32(self.central_directory_size)); try!(writer.write_u32::<LittleEndian>(self.central_directory_size));
try!(writer.write_le_u32(self.central_directory_offset)); try!(writer.write_u32::<LittleEndian>(self.central_directory_offset));
try!(writer.write_le_u16(self.zip_file_comment.len() as u16)); try!(writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16));
try!(writer.write_all(self.zip_file_comment.as_slice())); try!(writer.write_all(self.zip_file_comment.as_slice()));
Ok(()) Ok(())
} }

View file

@ -40,82 +40,3 @@ pub fn tm_to_msdos_date(time: Tm) -> u16
{ {
(time.tm_mday | ((time.tm_mon + 1) << 5) | ((time.tm_year - 80) << 9)) as u16 (time.tm_mday | ((time.tm_mon + 1) << 5) | ((time.tm_year - 80) << 9)) as u16
} }
/// Additional integer write methods for a io::Read
pub trait WriteIntExt {
/// Write a u32 in little-endian mode
fn write_le_u32(&mut self, u32) -> io::Result<()>;
/// Write a u16 in little-endian mode
fn write_le_u16(&mut self, u16) -> io::Result<()>;
}
impl<W: Write> WriteIntExt for W {
fn write_le_u32(&mut self, val: u32) -> io::Result<()> {
let mut buf = [0u8; 4];
let v = val;
buf[0] = ((v >> 0) & 0xFF) as u8;
buf[1] = ((v >> 8) & 0xFF) as u8;
buf[2] = ((v >> 16) & 0xFF) as u8;
buf[3] = ((v >> 24) & 0xFF) as u8;
self.write_all(&buf)
}
fn write_le_u16(&mut self, val: u16) -> io::Result<()> {
let mut buf = [0u8; 2];
let v = val;
buf[0] = ((v >> 0) & 0xFF) as u8;
buf[1] = ((v >> 8) & 0xFF) as u8;
self.write_all(&buf)
}
}
/// Additional integer write methods for a io::Read
pub trait ReadIntExt {
/// Read a u32 in little-endian mode
fn read_le_u32(&mut self) -> io::Result<u32>;
/// Read a u16 in little-endian mode
fn read_le_u16(&mut self) -> io::Result<u16>;
/// Read exactly n bytes
fn read_exact(&mut self, usize) -> io::Result<Vec<u8>>;
}
fn fill_exact<R: Read>(reader: &mut R, buf: &mut [u8]) -> io::Result<()> {
let mut idx = 0;
while idx < buf.len() {
match reader.read(&mut buf[idx..]) {
Err(v) => return Err(v),
Ok(0) => return Err(io::Error::new(io::ErrorKind::ResourceUnavailable, "Could not fill the buffer", None)),
Ok(i) => idx += i,
}
}
Ok(())
}
impl<R: Read> ReadIntExt for R {
fn read_le_u32(&mut self) -> io::Result<u32> {
let mut buf = [0u8; 4];
try!(fill_exact(self, &mut buf));
Ok(
buf[0] as u32
| ((buf[1] as u32) << 8)
| ((buf[2] as u32) << 16)
| ((buf[3] as u32) << 24)
)
}
fn read_le_u16(&mut self) -> io::Result<u16> {
let mut buf = [0u8; 2];
try!(fill_exact(self, &mut buf));
Ok(
buf[0] as u16
| ((buf[1] as u16) << 8)
)
}
fn read_exact(&mut self, n: usize) -> io::Result<Vec<u8>> {
let mut res = vec![0u8; n];
try!(fill_exact(self, &mut res));
Ok(res)
}
}

View file

@ -18,7 +18,7 @@ use flate2::write::DeflateEncoder;
use bzip2; use bzip2;
use bzip2::writer::BzCompressor; use bzip2::writer::BzCompressor;
use util; use util;
use util::WriteIntExt; use podio::{WritePodExt, LittleEndian};
enum GenericZipWriter<W: Write + io::Seek> enum GenericZipWriter<W: Write + io::Seek>
{ {
@ -285,19 +285,19 @@ impl<W: Write+io::Seek> GenericZipWriter<W>
fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()> fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
{ {
try!(writer.write_le_u32(spec::LOCAL_FILE_HEADER_SIGNATURE)); try!(writer.write_u32::<LittleEndian>(spec::LOCAL_FILE_HEADER_SIGNATURE));
try!(writer.write_le_u16(20)); try!(writer.write_u16::<LittleEndian>(20));
let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 }; let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
try!(writer.write_le_u16(flag)); try!(writer.write_u16::<LittleEndian>(flag));
try!(writer.write_le_u16(file.compression_method as u16)); try!(writer.write_u16::<LittleEndian>(file.compression_method as u16));
try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time))); try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_time(file.last_modified_time)));
try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time))); try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_date(file.last_modified_time)));
try!(writer.write_le_u32(file.crc32)); try!(writer.write_u32::<LittleEndian>(file.crc32));
try!(writer.write_le_u32(file.compressed_size as u32)); try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
try!(writer.write_le_u32(file.uncompressed_size as u32)); try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16)); try!(writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16));
let extra_field = try!(build_extra_field(file)); let extra_field = try!(build_extra_field(file));
try!(writer.write_le_u16(extra_field.len() as u16)); try!(writer.write_u16::<LittleEndian>(extra_field.len() as u16));
try!(writer.write_all(file.file_name.as_bytes())); try!(writer.write_all(file.file_name.as_bytes()));
try!(writer.write_all(extra_field.as_slice())); try!(writer.write_all(extra_field.as_slice()));
@ -308,33 +308,33 @@ fn update_local_file_header<T: Write+io::Seek>(writer: &mut T, file: &ZipFileDat
{ {
static CRC32_OFFSET : u64 = 14; static CRC32_OFFSET : u64 = 14;
try!(writer.seek(io::SeekFrom::Start(file.header_start + CRC32_OFFSET))); try!(writer.seek(io::SeekFrom::Start(file.header_start + CRC32_OFFSET)));
try!(writer.write_le_u32(file.crc32)); try!(writer.write_u32::<LittleEndian>(file.crc32));
try!(writer.write_le_u32(file.compressed_size as u32)); try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
try!(writer.write_le_u32(file.uncompressed_size as u32)); try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
Ok(()) Ok(())
} }
fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()> fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
{ {
try!(writer.write_le_u32(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE)); try!(writer.write_u32::<LittleEndian>(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE));
try!(writer.write_le_u16(0x14FF)); try!(writer.write_u16::<LittleEndian>(0x14FF));
try!(writer.write_le_u16(20)); try!(writer.write_u16::<LittleEndian>(20));
let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 }; let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
try!(writer.write_le_u16(flag)); try!(writer.write_u16::<LittleEndian>(flag));
try!(writer.write_le_u16(file.compression_method as u16)); try!(writer.write_u16::<LittleEndian>(file.compression_method as u16));
try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time))); try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_time(file.last_modified_time)));
try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time))); try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_date(file.last_modified_time)));
try!(writer.write_le_u32(file.crc32)); try!(writer.write_u32::<LittleEndian>(file.crc32));
try!(writer.write_le_u32(file.compressed_size as u32)); try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
try!(writer.write_le_u32(file.uncompressed_size as u32)); try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16)); try!(writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16));
let extra_field = try!(build_extra_field(file)); let extra_field = try!(build_extra_field(file));
try!(writer.write_le_u16(extra_field.len() as u16)); try!(writer.write_u16::<LittleEndian>(extra_field.len() as u16));
try!(writer.write_le_u16(0)); try!(writer.write_u16::<LittleEndian>(0));
try!(writer.write_le_u16(0)); try!(writer.write_u16::<LittleEndian>(0));
try!(writer.write_le_u16(0)); try!(writer.write_u16::<LittleEndian>(0));
try!(writer.write_le_u32(0)); try!(writer.write_u32::<LittleEndian>(0));
try!(writer.write_le_u32(file.header_start as u32)); try!(writer.write_u32::<LittleEndian>(file.header_start as u32));
try!(writer.write_all(file.file_name.as_bytes())); try!(writer.write_all(file.file_name.as_bytes()));
try!(writer.write_all(extra_field.as_slice())); try!(writer.write_all(extra_field.as_slice()));