async_zip/base/read/
mem.rs

1// Copyright (c) 2022 Harry [Majored] [hello@majored.pw]
2// MIT License (https://github.com/Majored/rs-async-zip/blob/main/LICENSE)
3
4//! A concurrent ZIP reader which acts over an owned vector of bytes.
5//!
6//! Concurrency is achieved as a result of:
7//! - Wrapping the provided vector of bytes within an [`Arc`] to allow shared ownership.
8//! - Wrapping this [`Arc`] around a [`Cursor`] when reading (as the [`Arc`] can deref and coerce into a `&[u8]`).
9//!
10//! ### Usage
11//! Unlike the [`seek`] module, we no longer hold a mutable reference to any inner reader which in turn, allows the
12//! construction of concurrent [`ZipEntryReader`]s. Though, note that each individual [`ZipEntryReader`] cannot be sent
13//! between thread boundaries due to the masked lifetime requirement. Therefore, the overarching [`ZipFileReader`]
14//! should be cloned and moved into those contexts when needed.
15//!
16//! ### Concurrent Example
17//! ```no_run
18//! # use async_zip::base::read::mem::ZipFileReader;
19//! # use async_zip::error::Result;
20//! # use futures_lite::io::AsyncReadExt;
21//! #
22//! async fn run() -> Result<()> {
23//!     let reader = ZipFileReader::new(Vec::new()).await?;
24//!     let result = tokio::join!(read(&reader, 0), read(&reader, 1));
25//!
26//!     let data_0 = result.0?;
27//!     let data_1 = result.1?;
28//!
29//!     // Use data within current scope.
30//!
31//!     Ok(())
32//! }
33//!
34//! async fn read(reader: &ZipFileReader, index: usize) -> Result<Vec<u8>> {
35//!     let mut entry = reader.reader_without_entry(index).await?;
36//!     let mut data = Vec::new();
37//!     entry.read_to_end(&mut data).await?;
38//!     Ok(data)
39//! }
40//! ```
41//!
42//! ### Parallel Example
43//! ```no_run
44//! # use async_zip::base::read::mem::ZipFileReader;
45//! # use async_zip::error::Result;
46//! # use futures_lite::io::AsyncReadExt;
47//! #
48//! async fn run() -> Result<()> {
49//!     let reader = ZipFileReader::new(Vec::new()).await?;
50//!
51//!     let handle_0 = tokio::spawn(read(reader.clone(), 0));
52//!     let handle_1 = tokio::spawn(read(reader.clone(), 1));
53//!
54//!     let data_0 = handle_0.await.expect("thread panicked")?;
55//!     let data_1 = handle_1.await.expect("thread panicked")?;
56//!
57//!     // Use data within current scope.
58//!
59//!     Ok(())
60//! }
61//!
62//! async fn read(reader: ZipFileReader, index: usize) -> Result<Vec<u8>> {
63//!     let mut entry = reader.reader_without_entry(index).await?;
64//!     let mut data = Vec::new();
65//!     entry.read_to_end(&mut data).await?;
66//!     Ok(data)
67//! }
68//! ```
69
70#[cfg(doc)]
71use crate::base::read::seek;
72
73use crate::base::read::io::entry::ZipEntryReader;
74use crate::error::{Result, ZipError};
75use crate::file::ZipFile;
76
77use std::sync::Arc;
78
79use futures_lite::io::Cursor;
80
81use super::io::entry::{WithEntry, WithoutEntry};
82
83struct Inner {
84    data: Vec<u8>,
85    file: ZipFile,
86}
87
88// A concurrent ZIP reader which acts over an owned vector of bytes.
89#[derive(Clone)]
90pub struct ZipFileReader {
91    inner: Arc<Inner>,
92}
93
94impl ZipFileReader {
95    /// Constructs a new ZIP reader from an owned vector of bytes.
96    pub async fn new(data: Vec<u8>) -> Result<ZipFileReader> {
97        let file = crate::base::read::file(Cursor::new(&data)).await?;
98        Ok(ZipFileReader::from_raw_parts(data, file))
99    }
100
101    /// Constructs a ZIP reader from an owned vector of bytes and ZIP file information derived from those bytes.
102    ///
103    /// Providing a [`ZipFile`] that wasn't derived from those bytes may lead to inaccurate parsing.
104    pub fn from_raw_parts(data: Vec<u8>, file: ZipFile) -> ZipFileReader {
105        ZipFileReader { inner: Arc::new(Inner { data, file }) }
106    }
107
108    /// Returns this ZIP file's information.
109    pub fn file(&self) -> &ZipFile {
110        &self.inner.file
111    }
112
113    /// Returns the raw bytes provided to the reader during construction.
114    pub fn data(&self) -> &[u8] {
115        &self.inner.data
116    }
117
118    /// Returns a new entry reader if the provided index is valid.
119    pub async fn reader_without_entry(&self, index: usize) -> Result<ZipEntryReader<'_, Cursor<&[u8]>, WithoutEntry>> {
120        let stored_entry = self.inner.file.entries.get(index).ok_or(ZipError::EntryIndexOutOfBounds)?;
121        let mut cursor = Cursor::new(&self.inner.data[..]);
122
123        stored_entry.seek_to_data_offset(&mut cursor).await?;
124
125        Ok(ZipEntryReader::new_with_owned(
126            cursor,
127            stored_entry.entry.compression(),
128            stored_entry.entry.compressed_size(),
129        ))
130    }
131
132    /// Returns a new entry reader if the provided index is valid.
133    pub async fn reader_with_entry(&self, index: usize) -> Result<ZipEntryReader<'_, Cursor<&[u8]>, WithEntry<'_>>> {
134        let stored_entry = self.inner.file.entries.get(index).ok_or(ZipError::EntryIndexOutOfBounds)?;
135        let mut cursor = Cursor::new(&self.inner.data[..]);
136
137        stored_entry.seek_to_data_offset(&mut cursor).await?;
138
139        let reader = ZipEntryReader::new_with_owned(
140            cursor,
141            stored_entry.entry.compression(),
142            stored_entry.entry.compressed_size(),
143        );
144
145        Ok(reader.into_with_entry(stored_entry))
146    }
147}