Convert reader to new IO

This commit is contained in:
Mathijs van de Nes 2015-02-24 16:02:16 +01:00
parent 69b38b3ce5
commit bb89f577c5
5 changed files with 97 additions and 43 deletions

View file

@ -1,6 +1,7 @@
//! Helper module to compute a CRC32 checksum
use std::old_io;
use std::io;
use std::io::prelude::*;
static CRC32_TABLE : [u32; 256] = [
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
@ -69,7 +70,7 @@ pub struct Crc32Reader<R>
check: u32,
}
impl<R: Reader> Crc32Reader<R>
impl<R> Crc32Reader<R>
{
/// Get a new Crc32Reader which check the inner reader against checksum.
pub fn new(inner: R, checksum: u32) -> Crc32Reader<R>
@ -88,19 +89,14 @@ impl<R: Reader> Crc32Reader<R>
}
}
impl<R: Reader> Reader for Crc32Reader<R>
impl<R: Read> Read for Crc32Reader<R>
{
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize>
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>
{
let count = match self.inner.read(buf)
{
Ok(0) if !self.check_matches() => { return Err(io::Error::new(io::ErrorKind::Other, "Invalid checksum", None)) },
Ok(n) => n,
Err(ref e) if e.kind == old_io::EndOfFile =>
{
return
if self.check_matches() { Err(e.clone()) }
else { Err(old_io::IoError { kind: old_io::OtherIoError, desc: "Invalid checksum", detail: None, }) }
},
Err(e) => return Err(e),
};
self.crc = update(self.crc, &buf[0..count]);

View file

@ -4,7 +4,8 @@ use compression::CompressionMethod;
use spec;
use reader_spec;
use result::{ZipResult, ZipError};
use std::old_io;
use std::io;
use std::io::prelude::*;
use std::cell::{RefCell, BorrowState};
use std::collections::HashMap;
use flate2::FlateReadExt;
@ -47,7 +48,7 @@ fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
Err(ZipError::UnsupportedZipFile(detail))
}
impl<T: Reader+Seek> ZipReader<T>
impl<T: Read+io::Seek> ZipReader<T>
{
/// Opens a ZIP file and parses the content headers.
pub fn new(mut reader: T) -> ZipResult<ZipReader<T>>
@ -56,13 +57,13 @@ impl<T: Reader+Seek> ZipReader<T>
if footer.disk_number != footer.disk_with_central_directory { return unsupported_zip_error("Support for multi-disk files is not implemented") }
let directory_start = footer.central_directory_offset as i64;
let directory_start = footer.central_directory_offset as u64;
let number_of_files = footer.number_of_files_on_this_disk as usize;
let mut files = Vec::with_capacity(number_of_files);
let mut names_map = HashMap::new();
try!(reader.seek(directory_start, old_io::SeekSet));
try!(reader.seek(io::SeekFrom::Start(directory_start)));
for _ in (0 .. number_of_files)
{
let file = try!(reader_spec::central_header_to_zip_file(&mut reader));
@ -88,23 +89,23 @@ impl<T: Reader+Seek> ZipReader<T>
/// Gets a reader for a contained zipfile.
///
/// May return `ReaderUnavailable` if there is another reader borrowed.
pub fn read_file<'a>(&'a self, file: &ZipFile) -> ZipResult<Box<Reader+'a>>
pub fn read_file<'a>(&'a self, file: &ZipFile) -> ZipResult<Box<Read+'a>>
{
let mut inner_reader = match self.inner.borrow_state()
{
BorrowState::Unused => self.inner.borrow_mut(),
_ => return Err(ZipError::ReaderUnavailable),
};
let pos = file.data_start as i64;
let pos = file.data_start as u64;
if file.encrypted
{
return unsupported_zip_error("Encrypted files are not supported")
}
try!(inner_reader.seek(pos, old_io::SeekSet));
try!(inner_reader.seek(io::SeekFrom::Start(pos)));
let refmut_reader = ::util::RefMutReader::new(inner_reader);
let limit_reader = old_io::util::LimitReader::new(refmut_reader, file.compressed_size as usize);
let limit_reader = refmut_reader.take(file.compressed_size as u64);
let reader = match file.compression_method
{
@ -114,25 +115,25 @@ impl<T: Reader+Seek> ZipReader<T>
Crc32Reader::new(
limit_reader,
file.crc32))
as Box<Reader>
as Box<Read>
},
CompressionMethod::Deflated =>
{
let deflate_reader = IoConverter::new(IoConverter::new(limit_reader).deflate_decode());
let deflate_reader = limit_reader.deflate_decode();
Box::new(
Crc32Reader::new(
deflate_reader,
file.crc32))
as Box<Reader>
as Box<Read>
},
CompressionMethod::Bzip2 =>
{
let bzip2_reader = BzDecompressor::new(limit_reader);
let bzip2_reader = IoConverter::new(BzDecompressor::new(IoConverter::new(limit_reader)));
Box::new(
Crc32Reader::new(
bzip2_reader,
file.crc32))
as Box<Reader>
as Box<Read>
},
_ => return unsupported_zip_error("Compression method not supported"),
};

View file

@ -1,12 +1,14 @@
use std::old_io;
use std::io;
use std::io::prelude::*;
use std::num::FromPrimitive;
use result::{ZipResult, ZipError};
use types::ZipFile;
use compression::CompressionMethod;
use spec;
use util;
use util::ReadIntExt;
pub fn central_header_to_zip_file<R: Reader+Seek>(reader: &mut R) -> ZipResult<ZipFile>
pub fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R) -> ZipResult<ZipFile>
{
// Parse central header
let signature = try!(reader.read_le_u32());
@ -32,7 +34,7 @@ pub fn central_header_to_zip_file<R: Reader+Seek>(reader: &mut R) -> ZipResult<Z
try!(reader.read_le_u16());
try!(reader.read_le_u16());
try!(reader.read_le_u32());
let offset = try!(reader.read_le_u32()) as i64;
let offset = try!(reader.read_le_u32()) 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));
@ -49,17 +51,17 @@ pub fn central_header_to_zip_file<R: Reader+Seek>(reader: &mut R) -> ZipResult<Z
};
// Remember end of central header
let return_position = try!(reader.tell()) as i64;
let return_position = try!(reader.seek(io::SeekFrom::Current(0)));
// Parse local header
try!(reader.seek(offset, old_io::SeekSet));
try!(reader.seek(io::SeekFrom::Start(offset)));
let signature = try!(reader.read_le_u32());
if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
{
return Err(ZipError::InvalidZipFile("Invalid local file header"))
}
try!(reader.seek(22, old_io::SeekCur));
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 magic_and_header = 4 + 22 + 2 + 2;
@ -83,22 +85,23 @@ pub fn central_header_to_zip_file<R: Reader+Seek>(reader: &mut R) -> ZipResult<Z
try!(parse_extra_field(&mut result, &*extra_field));
// Go back after the central header
try!(reader.seek(return_position, old_io::SeekSet));
try!(reader.seek(io::SeekFrom::Start(return_position)));
Ok(result)
}
fn parse_extra_field(_file: &mut ZipFile, data: &[u8]) -> ZipResult<()>
{
let mut reader = old_io::BufReader::new(data);
while !reader.eof()
let mut reader = io::Cursor::new(data);
while (reader.position() as usize) < data.len()
{
let kind = try!(reader.read_le_u16());
let len = try!(reader.read_le_u16());
match kind
{
_ => try!(reader.seek(len as i64, old_io::SeekCur)),
}
_ => try!(reader.seek(io::SeekFrom::Current(len as i64))),
};
}
Ok(())
}

View file

@ -1,8 +1,8 @@
use std::old_io;
use std::io;
use std::io::prelude::*;
use result::{ZipResult, ZipError};
use std::iter::range_step_inclusive;
use util::WriteIntExt;
use util::{ReadIntExt, WriteIntExt};
pub static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
pub static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
@ -21,7 +21,7 @@ pub struct CentralDirectoryEnd
impl CentralDirectoryEnd
{
pub fn parse<T: Reader>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
{
let magic = try!(reader.read_le_u32());
if magic != CENTRAL_DIRECTORY_END_SIGNATURE
@ -49,24 +49,23 @@ impl CentralDirectoryEnd
})
}
pub fn find_and_parse<T: Reader+Seek>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
pub fn find_and_parse<T: Read+io::Seek>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
{
let header_size = 22;
let bytes_between_magic_and_comment_size = header_size - 6;
try!(reader.seek(0, old_io::SeekEnd));
let file_length = try!(reader.tell()) as i64;
let file_length = try!(reader.seek(io::SeekFrom::End(0))) as i64;
let search_upper_bound = ::std::cmp::max(0, file_length - header_size - ::std::u16::MAX as i64);
for pos in range_step_inclusive(file_length - header_size, search_upper_bound, -1)
{
try!(reader.seek(pos, old_io::SeekSet));
try!(reader.seek(io::SeekFrom::Start(pos as u64)));
if try!(reader.read_le_u32()) == CENTRAL_DIRECTORY_END_SIGNATURE
{
try!(reader.seek(bytes_between_magic_and_comment_size, old_io::SeekCur));
try!(reader.seek(io::SeekFrom::Current(bytes_between_magic_and_comment_size)));
let comment_length = try!(reader.read_le_u16()) as i64;
if file_length - pos - header_size == comment_length
{
try!(reader.seek(pos, old_io::SeekSet));
try!(reader.seek(io::SeekFrom::Start(pos as u64)));
return CentralDirectoryEnd::parse(reader);
}
}

View file

@ -98,3 +98,58 @@ impl<W: Write> WriteIntExt for W {
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;
let mut tries = 0;
while idx < buf.len() {
match reader.read(&mut buf[idx..]) {
Err(v) => return Err(v),
Ok(i) => idx += i,
}
tries += 1;
if tries > 2*buf.len() {
return Err(io::Error::new(io::ErrorKind::ResourceUnavailable, "Could not fill the buffer", None));
}
}
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)
}
}