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"
bzip2 = "^0.2"
time = "*"
podio = "^0.0"

View file

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

View file

@ -12,7 +12,7 @@ use flate2;
use flate2::FlateReadExt;
use bzip2::reader::BzDecompressor;
use util;
use util::ReadIntExt;
use podio::{ReadPodExt, LittleEndian};
use types::ZipFileData;
/// 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>
{
// Parse central header
let signature = try!(reader.read_le_u32());
let signature = try!(reader.read_u32::<LittleEndian>());
if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
{
return Err(ZipError::InvalidArchive("Invalid Central Directory header"))
}
try!(reader.read_le_u16());
try!(reader.read_le_u16());
let flags = try!(reader.read_le_u16());
try!(reader.read_u16::<LittleEndian>());
try!(reader.read_u16::<LittleEndian>());
let flags = try!(reader.read_u16::<LittleEndian>());
let encrypted = flags & 1 == 1;
let is_utf8 = flags & (1 << 11) != 0;
let compression_method = try!(reader.read_le_u16());
let last_mod_time = try!(reader.read_le_u16());
let last_mod_date = try!(reader.read_le_u16());
let crc32 = try!(reader.read_le_u32());
let compressed_size = try!(reader.read_le_u32());
let uncompressed_size = try!(reader.read_le_u32());
let file_name_length = try!(reader.read_le_u16()) as usize;
let extra_field_length = try!(reader.read_le_u16()) as usize;
let file_comment_length = try!(reader.read_le_u16()) as usize;
try!(reader.read_le_u16());
try!(reader.read_le_u16());
try!(reader.read_le_u32());
let offset = try!(reader.read_le_u32()) as u64;
let compression_method = try!(reader.read_u16::<LittleEndian>());
let last_mod_time = try!(reader.read_u16::<LittleEndian>());
let last_mod_date = try!(reader.read_u16::<LittleEndian>());
let crc32 = try!(reader.read_u32::<LittleEndian>());
let compressed_size = try!(reader.read_u32::<LittleEndian>());
let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
let file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
try!(reader.read_u16::<LittleEndian>());
try!(reader.read_u16::<LittleEndian>());
try!(reader.read_u32::<LittleEndian>());
let offset = try!(reader.read_u32::<LittleEndian>()) as u64;
let file_name_raw = try!(reader.read_exact(file_name_length));
let extra_field = try!(reader.read_exact(extra_field_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
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
{
return Err(ZipError::InvalidArchive("Invalid local file header"))
}
try!(reader.seek(io::SeekFrom::Current(22)));
let file_name_length = try!(reader.read_le_u16()) as u64;
let extra_field_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_u16::<LittleEndian>()) as u64;
let magic_and_header = 4 + 22 + 2 + 2;
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()
{
let kind = try!(reader.read_le_u16());
let len = try!(reader.read_le_u16());
let kind = try!(reader.read_u16::<LittleEndian>());
let len = try!(reader.read_u16::<LittleEndian>());
match kind
{
_ => try!(reader.seek(io::SeekFrom::Current(len as i64))),

View file

@ -2,7 +2,7 @@ use std::io;
use std::io::prelude::*;
use result::{ZipResult, ZipError};
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 CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
@ -23,18 +23,18 @@ impl 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
{
return Err(ZipError::InvalidArchive("Invalid digital signature header"))
}
let disk_number = try!(reader.read_le_u16());
let disk_with_central_directory = try!(reader.read_le_u16());
let number_of_files_on_this_disk = try!(reader.read_le_u16());
let number_of_files = try!(reader.read_le_u16());
let central_directory_size = try!(reader.read_le_u32());
let central_directory_offset = try!(reader.read_le_u32());
let zip_file_comment_length = try!(reader.read_le_u16()) as usize;
let disk_number = try!(reader.read_u16::<LittleEndian>());
let disk_with_central_directory = try!(reader.read_u16::<LittleEndian>());
let number_of_files_on_this_disk = try!(reader.read_u16::<LittleEndian>());
let number_of_files = try!(reader.read_u16::<LittleEndian>());
let central_directory_size = try!(reader.read_u32::<LittleEndian>());
let central_directory_offset = try!(reader.read_u32::<LittleEndian>());
let zip_file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
let zip_file_comment = try!(reader.read_exact(zip_file_comment_length));
Ok(CentralDirectoryEnd
@ -59,10 +59,10 @@ impl CentralDirectoryEnd
for pos in range_step_inclusive(file_length - header_size, search_upper_bound, -1)
{
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)));
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
{
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<()>
{
try!(writer.write_le_u32(CENTRAL_DIRECTORY_END_SIGNATURE));
try!(writer.write_le_u16(self.disk_number));
try!(writer.write_le_u16(self.disk_with_central_directory));
try!(writer.write_le_u16(self.number_of_files_on_this_disk));
try!(writer.write_le_u16(self.number_of_files));
try!(writer.write_le_u32(self.central_directory_size));
try!(writer.write_le_u32(self.central_directory_offset));
try!(writer.write_le_u16(self.zip_file_comment.len() as u16));
try!(writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE));
try!(writer.write_u16::<LittleEndian>(self.disk_number));
try!(writer.write_u16::<LittleEndian>(self.disk_with_central_directory));
try!(writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk));
try!(writer.write_u16::<LittleEndian>(self.number_of_files));
try!(writer.write_u32::<LittleEndian>(self.central_directory_size));
try!(writer.write_u32::<LittleEndian>(self.central_directory_offset));
try!(writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16));
try!(writer.write_all(self.zip_file_comment.as_slice()));
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
}
/// 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::writer::BzCompressor;
use util;
use util::WriteIntExt;
use podio::{WritePodExt, LittleEndian};
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<()>
{
try!(writer.write_le_u32(spec::LOCAL_FILE_HEADER_SIGNATURE));
try!(writer.write_le_u16(20));
try!(writer.write_u32::<LittleEndian>(spec::LOCAL_FILE_HEADER_SIGNATURE));
try!(writer.write_u16::<LittleEndian>(20));
let flag = if !file.file_name.is_ascii() { 1u16 << 11 } 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)));
try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time)));
try!(writer.write_le_u32(file.crc32));
try!(writer.write_le_u32(file.compressed_size as u32));
try!(writer.write_le_u32(file.uncompressed_size as u32));
try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16));
try!(writer.write_u16::<LittleEndian>(flag));
try!(writer.write_u16::<LittleEndian>(file.compression_method as u16));
try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_time(file.last_modified_time)));
try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_date(file.last_modified_time)));
try!(writer.write_u32::<LittleEndian>(file.crc32));
try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
try!(writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16));
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(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;
try!(writer.seek(io::SeekFrom::Start(file.header_start + CRC32_OFFSET)));
try!(writer.write_le_u32(file.crc32));
try!(writer.write_le_u32(file.compressed_size as u32));
try!(writer.write_le_u32(file.uncompressed_size as u32));
try!(writer.write_u32::<LittleEndian>(file.crc32));
try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
Ok(())
}
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_le_u16(0x14FF));
try!(writer.write_le_u16(20));
try!(writer.write_u32::<LittleEndian>(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE));
try!(writer.write_u16::<LittleEndian>(0x14FF));
try!(writer.write_u16::<LittleEndian>(20));
let flag = if !file.file_name.is_ascii() { 1u16 << 11 } 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)));
try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time)));
try!(writer.write_le_u32(file.crc32));
try!(writer.write_le_u32(file.compressed_size as u32));
try!(writer.write_le_u32(file.uncompressed_size as u32));
try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16));
try!(writer.write_u16::<LittleEndian>(flag));
try!(writer.write_u16::<LittleEndian>(file.compression_method as u16));
try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_time(file.last_modified_time)));
try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_date(file.last_modified_time)));
try!(writer.write_u32::<LittleEndian>(file.crc32));
try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
try!(writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16));
let extra_field = try!(build_extra_field(file));
try!(writer.write_le_u16(extra_field.len() as u16));
try!(writer.write_le_u16(0));
try!(writer.write_le_u16(0));
try!(writer.write_le_u16(0));
try!(writer.write_le_u32(0));
try!(writer.write_le_u32(file.header_start as u32));
try!(writer.write_u16::<LittleEndian>(extra_field.len() as u16));
try!(writer.write_u16::<LittleEndian>(0));
try!(writer.write_u16::<LittleEndian>(0));
try!(writer.write_u16::<LittleEndian>(0));
try!(writer.write_u32::<LittleEndian>(0));
try!(writer.write_u32::<LittleEndian>(file.header_start as u32));
try!(writer.write_all(file.file_name.as_bytes()));
try!(writer.write_all(extra_field.as_slice()));