Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rc-zip-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ fn info(out: &mut impl io::Write, archive: &Archive) -> io::Result<()> {

fn list(
out: &mut impl io::Write,
archive: &ArchiveHandle<'_, File>,
archive: &ArchiveHandle<File>,
verbose: bool,
) -> io::Result<()> {
for entry in archive.entries() {
Expand Down
6 changes: 3 additions & 3 deletions rc-zip-sync/benches/read.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{hint::black_box, time::Duration};
use std::{hint::black_box, ops::Deref, time::Duration};

use divan::{bench, counter::ItemsCount, Bencher, Divan};
use rc_zip_corpus::test_cases;
Expand All @@ -15,10 +15,10 @@ fn main() {
fn archive_entries(bencher: Bencher, name: &'static str) {
let case = test_cases().into_iter().find(|c| c.name == name).unwrap();
let zip_contents = case.bytes();
let archive = zip_contents.read_zip().unwrap();
let archive = zip_contents.deref().read_zip().unwrap();
let num_entries = ItemsCount::new(archive.entries().count());

bencher
.counter(num_entries)
.bench(|| black_box(&zip_contents).read_zip().unwrap());
.bench(|| black_box(zip_contents.deref()).read_zip().unwrap());
}
2 changes: 1 addition & 1 deletion rc-zip-sync/examples/byte_count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl Counts {
/// The work is split across a pool of worker threads where each worker takes turns fetching an
/// entry from the archive to then read over the entry and reduce it down to counts. Then the final
/// counts are totaled together as each worker finishes.
fn byte_count_multi_threaded(archive: &ArchiveHandle<'_, File>) -> Counts {
fn byte_count_multi_threaded(archive: &ArchiveHandle<File>) -> Counts {
let mut total_counts = Counts::new();
let entries = Mutex::new(archive.entries());
let num_workers = thread::available_parallelism().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion rc-zip-sync/examples/self_extracting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fn main() -> Result<(), Error> {
Ok(())
}

fn extract(archive: &ArchiveHandle<'_, File>) -> Result<(), Error> {
fn extract(archive: &ArchiveHandle<File>) -> Result<(), Error> {
for entry in archive.entries() {
println!("extracting {}", entry.name);
let Some(entry_name) = entry.sanitized_name() else {
Expand Down
184 changes: 128 additions & 56 deletions rc-zip-sync/src/read_zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use tracing::trace;

use crate::entry_reader::EntryReader;
use crate::streaming_entry_reader::StreamingEntryReader;
use std::{io::Read, ops::Deref};
use std::{io::Read, ops::Deref, sync::Arc};

/// A trait for reading something as a zip archive
///
Expand All @@ -16,7 +16,7 @@ pub trait ReadZipWithSize {
type File: HasCursor;

/// Reads self as a zip archive.
fn read_zip_with_size(&self, size: u64) -> Result<ArchiveHandle<'_, Self::File>, Error>;
fn read_zip_with_size(self, size: u64) -> Result<ArchiveHandle<Self::File>, Error>;
}

/// A trait for reading something as a zip archive when we can tell size from
Expand All @@ -28,7 +28,7 @@ pub trait ReadZip {
type File: HasCursor;

/// Reads self as a zip archive.
fn read_zip(&self) -> Result<ArchiveHandle<'_, Self::File>, Error>;
fn read_zip(self) -> Result<ArchiveHandle<Self::File>, Error>;
}

struct CursorState<'a, F: HasCursor + 'a> {
Expand All @@ -52,73 +52,94 @@ impl<'a, F: HasCursor + 'a> CursorState<'a, F> {

impl<F> ReadZipWithSize for F
where
F: HasCursor,
F: HasCursor + Sized,
{
type File = F;

fn read_zip_with_size(&self, size: u64) -> Result<ArchiveHandle<'_, F>, Error> {
let mut cstate: Option<CursorState<'_, F>> = None;

let mut fsm = ArchiveFsm::new(size);
loop {
if let Some(offset) = fsm.wants_read() {
trace!(%offset, "read_zip_with_size: wants_read, space len = {}", fsm.space().len());

let mut cstate_next = match cstate.take() {
// all good, re-using
Some(cstate) if cstate.offset == offset => cstate,
Some(cstate) => {
trace!(%offset, %cstate.offset, "read_zip_with_size: making new cursor (had wrong offset)");
CursorState::try_new(self, offset, size)?
}
None => {
trace!(%offset, "read_zip_with_size: making new cursor (had none)");
CursorState::try_new(self, offset, size)?
}
};

match cstate_next.cursor.read(fsm.space()) {
Ok(read_bytes) => {
cstate_next.offset += read_bytes as u64;
cstate = Some(cstate_next);

trace!(%read_bytes, "read_zip_with_size: read");
if read_bytes == 0 {
return Err(Error::IO(std::io::ErrorKind::UnexpectedEof.into()));
fn read_zip_with_size(self, size: u64) -> Result<ArchiveHandle<F>, Error> {
let archive = {
let mut cstate: Option<CursorState<'_, F>> = None;
let mut fsm = ArchiveFsm::new(size);
loop {
if let Some(offset) = fsm.wants_read() {
trace!(%offset, "read_zip_with_size: wants_read, space len = {}", fsm.space().len());

let mut cstate_next = match cstate.take() {
// all good, re-using
Some(cstate) if cstate.offset == offset => cstate,
Some(cstate) => {
trace!(%offset, %cstate.offset, "read_zip_with_size: making new cursor (had wrong offset)");
CursorState::try_new(&self, offset, size)?
}
None => {
trace!(%offset, "read_zip_with_size: making new cursor (had none)");
CursorState::try_new(&self, offset, size)?
}
fsm.fill(read_bytes);
};

match cstate_next.cursor.read(fsm.space()) {
Ok(read_bytes) => {
cstate_next.offset += read_bytes as u64;
cstate = Some(cstate_next);

trace!(%read_bytes, "read_zip_with_size: read");
if read_bytes == 0 {
return Err(Error::IO(std::io::ErrorKind::UnexpectedEof.into()));
}
fsm.fill(read_bytes);
}
Err(err) => return Err(Error::IO(err)),
}
Err(err) => return Err(Error::IO(err)),
}
}

fsm = match fsm.process()? {
FsmResult::Done(archive) => {
trace!("read_zip_with_size: done");
return Ok(ArchiveHandle {
file: self,
archive,
});
fsm = match fsm.process()? {
FsmResult::Done(archive) => {
trace!("read_zip_with_size: done");
break archive;
}
FsmResult::Continue(fsm) => fsm,
}
FsmResult::Continue(fsm) => fsm,
}
}
};
return Ok(ArchiveHandle {
file: self,
archive,
});
}
}

impl ReadZip for &[u8] {
type File = Self;

fn read_zip(&self) -> Result<ArchiveHandle<'_, Self::File>, Error> {
fn read_zip(self) -> Result<ArchiveHandle<Self::File>, Error> {
self.read_zip_with_size(self.len() as u64)
}
}

impl ReadZip for Vec<u8> {
type File = Self;

fn read_zip(&self) -> Result<ArchiveHandle<'_, Self::File>, Error> {
self.read_zip_with_size(self.len() as u64)
fn read_zip(self) -> Result<ArchiveHandle<Self::File>, Error> {
let len = self.len();
self.read_zip_with_size(len as u64)
}
}

impl ReadZip for Box<[u8]> {
type File = Self;

fn read_zip(self) -> Result<ArchiveHandle<Self::File>, Error> {
let len = self.len();
self.read_zip_with_size(len as u64)
}
}

impl ReadZip for Arc<[u8]> {
type File = Self;

fn read_zip(self) -> Result<ArchiveHandle<Self::File>, Error> {
let len = self.len();
self.read_zip_with_size(len as u64)
}
}

Expand All @@ -127,15 +148,15 @@ impl ReadZip for Vec<u8> {
/// This only contains metadata for the archive and its entries. Separate
/// readers can be created for arbitraries entries on-demand using
/// [EntryHandle::reader].
pub struct ArchiveHandle<'a, F>
pub struct ArchiveHandle<F>
where
F: HasCursor,
{
file: &'a F,
file: F,
archive: Archive,
}

impl<F> Deref for ArchiveHandle<'_, F>
impl<F> Deref for ArchiveHandle<F>
where
F: HasCursor,
{
Expand All @@ -146,14 +167,14 @@ where
}
}

impl<F> ArchiveHandle<'_, F>
impl<F> ArchiveHandle<F>
where
F: HasCursor,
{
/// Iterate over all files in this zip, read from the central directory.
pub fn entries(&self) -> impl Iterator<Item = EntryHandle<'_, F>> {
self.archive.entries().map(move |entry| EntryHandle {
file: self.file,
file: &self.file,
entry,
})
}
Expand All @@ -165,7 +186,7 @@ where
.entries()
.find(|&x| x.name == name.as_ref())
.map(|entry| EntryHandle {
file: self.file,
file: &self.file,
entry,
})
}
Expand Down Expand Up @@ -214,7 +235,7 @@ pub trait HasCursor {
fn cursor_at(&self, offset: u64) -> Self::Cursor<'_>;
}

impl HasCursor for &[u8] {
impl HasCursor for [u8] {
type Cursor<'a>
= &'a [u8]
where
Expand All @@ -236,6 +257,37 @@ impl HasCursor for Vec<u8> {
}
}

impl<T: HasCursor + ?Sized> HasCursor for &T {
type Cursor<'a>
= T::Cursor<'a>
where
Self: 'a;
fn cursor_at(&self, offset: u64) -> Self::Cursor<'_> {
let inner: &T = self;
inner.cursor_at(offset)
}
}

impl<T: HasCursor + ?Sized> HasCursor for Arc<T> {
type Cursor<'a>
= T::Cursor<'a>
where
Self: 'a;
fn cursor_at(&self, offset: u64) -> Self::Cursor<'_> {
self.deref().cursor_at(offset)
}
}

impl<T: HasCursor + ?Sized> HasCursor for Box<T> {
type Cursor<'a>
= T::Cursor<'a>
where
Self: 'a;
fn cursor_at(&self, offset: u64) -> Self::Cursor<'_> {
self.deref().cursor_at(offset)
}
}

#[cfg(feature = "file")]
impl HasCursor for std::fs::File {
type Cursor<'a>
Expand All @@ -252,7 +304,27 @@ impl HasCursor for std::fs::File {
impl ReadZip for std::fs::File {
type File = Self;

fn read_zip(&self) -> Result<ArchiveHandle<'_, Self>, Error> {
fn read_zip(self) -> Result<ArchiveHandle<Self>, Error> {
let size = self.metadata()?.len();
self.read_zip_with_size(size)
}
}

#[cfg(feature = "file")]
impl ReadZip for &std::fs::File {
type File = Self;

fn read_zip(self) -> Result<ArchiveHandle<Self>, Error> {
let size = self.metadata()?.len();
self.read_zip_with_size(size)
}
}

#[cfg(feature = "file")]
impl ReadZip for Arc<std::fs::File> {
type File = Self;

fn read_zip(self) -> Result<ArchiveHandle<Self>, Error> {
let size = self.metadata()?.len();
self.read_zip_with_size(size)
}
Expand Down
2 changes: 1 addition & 1 deletion rc-zip-sync/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
io::{self, Read},
};

fn check_case<F: HasCursor>(test: &Case, archive: Result<ArchiveHandle<'_, F>, Error>) {
fn check_case<F: HasCursor>(test: &Case, archive: Result<ArchiveHandle<F>, Error>) {
rc_zip_corpus::check_case(test, archive.as_ref().map(|ar| -> &Archive { ar }));
let archive = match archive {
Ok(archive) => archive,
Expand Down
Loading