use crate::error::{Result, SZipError};
use crate::writer::CompressionMethod;
use async_compression::tokio::write::DeflateEncoder;
#[cfg(feature = "async-zstd")]
use async_compression::tokio::write::ZstdEncoder;
use crc32fast::Hasher as Crc32;
use std::io::Write;
use std::path::Path;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt};
#[cfg(feature = "encryption")]
use crate::encryption::{AesEncryptor, AesStrength};
struct ZipEntry {
name: String,
local_header_offset: u64,
crc32: u32,
compressed_size: u64,
uncompressed_size: u64,
compression_method: u16,
#[cfg(feature = "encryption")]
encryption_strength: Option<u16>,
}
pub struct AsyncStreamingZipWriter<W: AsyncWrite + AsyncSeek + Unpin> {
output: W,
entries: Vec<ZipEntry>,
current_entry: Option<CurrentEntry>,
compression_level: u32,
compression_method: CompressionMethod,
#[cfg(feature = "encryption")]
password: Option<String>,
#[cfg(feature = "encryption")]
encryption_strength: AesStrength,
}
struct CurrentEntry {
name: String,
local_header_offset: u64,
encoder: Box<dyn AsyncCompressorWrite>,
counter: CrcCounter,
compression_method: u16,
#[cfg(feature = "encryption")]
encryptor: Option<AesEncryptor>,
}
trait AsyncCompressorWrite: AsyncWrite + Unpin + Send {
fn finish_compression(
self: Box<Self>,
) -> Pin<Box<dyn std::future::Future<Output = Result<CompressedBuffer>> + Send>>;
fn get_buffer_mut(&mut self) -> &mut CompressedBuffer;
}
struct DeflateCompressor {
encoder: DeflateEncoder<CompressedBuffer>,
}
impl AsyncWrite for DeflateCompressor {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
Pin::new(&mut self.encoder).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.encoder).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.encoder).poll_shutdown(cx)
}
}
impl AsyncCompressorWrite for DeflateCompressor {
fn finish_compression(
mut self: Box<Self>,
) -> Pin<Box<dyn std::future::Future<Output = Result<CompressedBuffer>> + Send>> {
Box::pin(async move {
self.encoder.shutdown().await?;
Ok(self.encoder.into_inner())
})
}
fn get_buffer_mut(&mut self) -> &mut CompressedBuffer {
self.encoder.get_mut()
}
}
#[cfg(feature = "async-zstd")]
struct ZstdCompressor {
encoder: ZstdEncoder<CompressedBuffer>,
}
#[cfg(feature = "async-zstd")]
impl AsyncWrite for ZstdCompressor {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
Pin::new(&mut self.encoder).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.encoder).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.encoder).poll_shutdown(cx)
}
}
#[cfg(feature = "async-zstd")]
impl AsyncCompressorWrite for ZstdCompressor {
fn finish_compression(
mut self: Box<Self>,
) -> Pin<Box<dyn std::future::Future<Output = Result<CompressedBuffer>> + Send>> {
Box::pin(async move {
self.encoder.shutdown().await?;
Ok(self.encoder.into_inner())
})
}
fn get_buffer_mut(&mut self) -> &mut CompressedBuffer {
self.encoder.get_mut()
}
}
struct CrcCounter {
crc: Crc32,
uncompressed_count: u64,
compressed_count: u64,
}
impl CrcCounter {
fn new() -> Self {
Self {
crc: Crc32::new(),
uncompressed_count: 0,
compressed_count: 0,
}
}
fn update_uncompressed(&mut self, data: &[u8]) {
self.crc.update(data);
self.uncompressed_count += data.len() as u64;
}
fn add_compressed(&mut self, count: u64) {
self.compressed_count += count;
}
fn finalize(&self) -> u32 {
self.crc.clone().finalize()
}
}
pub struct CompressedBuffer {
buffer: Vec<u8>,
flush_threshold: usize,
}
impl CompressedBuffer {
#[allow(dead_code)]
fn new() -> Self {
Self::with_size_hint(None)
}
fn with_size_hint(size_hint: Option<u64>) -> Self {
let (initial_capacity, flush_threshold) = match size_hint {
Some(size) if size < 10_000 => (8 * 1024, 256 * 1024), Some(size) if size < 100_000 => (32 * 1024, 512 * 1024), Some(size) if size < 1_000_000 => (128 * 1024, 2 * 1024 * 1024), Some(size) if size < 10_000_000 => (256 * 1024, 4 * 1024 * 1024), _ => (512 * 1024, 8 * 1024 * 1024), };
Self {
buffer: Vec::with_capacity(initial_capacity),
flush_threshold,
}
}
fn take(&mut self) -> Vec<u8> {
std::mem::take(&mut self.buffer)
}
fn should_flush(&self) -> bool {
self.buffer.len() >= self.flush_threshold
}
}
impl Write for CompressedBuffer {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.buffer.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl AsyncWrite for CompressedBuffer {
fn poll_write(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
self.buffer.extend_from_slice(buf);
Poll::Ready(Ok(buf.len()))
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
}
}
impl AsyncStreamingZipWriter<tokio::fs::File> {
pub async fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
Self::with_compression(path, 6).await
}
pub async fn with_compression<P: AsRef<Path>>(path: P, compression_level: u32) -> Result<Self> {
Self::with_method(path, CompressionMethod::Deflate, compression_level).await
}
pub async fn with_method<P: AsRef<Path>>(
path: P,
method: CompressionMethod,
compression_level: u32,
) -> Result<Self> {
let output = tokio::fs::File::create(path).await?;
Ok(Self {
output,
entries: Vec::new(),
current_entry: None,
compression_level,
compression_method: method,
#[cfg(feature = "encryption")]
password: None,
#[cfg(feature = "encryption")]
encryption_strength: AesStrength::Aes256,
})
}
#[cfg(feature = "async-zstd")]
pub async fn with_zstd<P: AsRef<Path>>(path: P, compression_level: i32) -> Result<Self> {
let output = tokio::fs::File::create(path).await?;
Ok(Self {
output,
entries: Vec::new(),
current_entry: None,
compression_level: compression_level as u32,
compression_method: CompressionMethod::Zstd,
#[cfg(feature = "encryption")]
password: None,
#[cfg(feature = "encryption")]
encryption_strength: AesStrength::Aes256,
})
}
}
struct StoredCompressor {
buffer: CompressedBuffer,
}
impl AsyncWrite for StoredCompressor {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
Pin::new(&mut self.buffer).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.buffer).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.buffer).poll_shutdown(cx)
}
}
impl AsyncCompressorWrite for StoredCompressor {
fn finish_compression(
self: Box<Self>,
) -> Pin<Box<dyn std::future::Future<Output = Result<CompressedBuffer>> + Send>> {
Box::pin(async move { Ok(self.buffer) })
}
fn get_buffer_mut(&mut self) -> &mut CompressedBuffer {
&mut self.buffer
}
}
impl<W: AsyncWrite + AsyncSeek + Unpin> AsyncStreamingZipWriter<W> {
pub fn from_writer(writer: W) -> Self {
Self::from_writer_with_compression(writer, 6)
}
pub fn from_writer_with_compression(writer: W, compression_level: u32) -> Self {
Self::from_writer_with_method(writer, CompressionMethod::Deflate, compression_level)
}
pub fn from_writer_with_method(
writer: W,
method: CompressionMethod,
compression_level: u32,
) -> Self {
Self {
output: writer,
entries: Vec::new(),
current_entry: None,
compression_level,
compression_method: method,
#[cfg(feature = "encryption")]
password: None,
#[cfg(feature = "encryption")]
encryption_strength: AesStrength::Aes256,
}
}
#[cfg(feature = "encryption")]
pub fn set_password(&mut self, password: impl Into<String>) -> &mut Self {
self.password = Some(password.into());
self
}
#[cfg(feature = "encryption")]
pub fn set_encryption_strength(&mut self, strength: AesStrength) -> &mut Self {
self.encryption_strength = strength;
self
}
#[cfg(feature = "encryption")]
pub fn clear_password(&mut self) -> &mut Self {
self.password = None;
self
}
pub async fn start_entry(&mut self, name: &str) -> Result<()> {
self.start_entry_with_hint(name, None).await
}
pub async fn start_entry_with_hint(
&mut self,
name: &str,
size_hint: Option<u64>,
) -> Result<()> {
self.finish_current_entry().await?;
let local_header_offset = self.output.stream_position().await?;
let compression_method = self.compression_method.to_zip_method();
#[cfg(feature = "encryption")]
let (encryptor, encryption_flag) = if let Some(ref password) = self.password {
let enc = AesEncryptor::new(password, self.encryption_strength)?;
(Some(enc), 0x01) } else {
(None, 0x00)
};
#[cfg(not(feature = "encryption"))]
let encryption_flag = 0x00;
self.output.write_all(&[0x50, 0x4b, 0x03, 0x04]).await?; self.output.write_all(&[51, 0]).await?; self.output.write_all(&[8 | encryption_flag, 0]).await?; self.output
.write_all(&compression_method.to_le_bytes())
.await?; self.output.write_all(&[0, 0, 0, 0]).await?; self.output.write_all(&0u32.to_le_bytes()).await?; self.output.write_all(&0u32.to_le_bytes()).await?; self.output.write_all(&0u32.to_le_bytes()).await?; self.output
.write_all(&(name.len() as u16).to_le_bytes())
.await?;
#[cfg(feature = "encryption")]
let extra_len = if encryptor.is_some() { 11 } else { 0 };
#[cfg(not(feature = "encryption"))]
let extra_len = 0;
self.output
.write_all(&(extra_len as u16).to_le_bytes())
.await?; self.output.write_all(name.as_bytes()).await?;
#[cfg(feature = "encryption")]
if let Some(ref enc) = encryptor {
self.output.write_all(&[0x01, 0x99]).await?; self.output.write_all(&[7, 0]).await?; self.output.write_all(&[2, 0]).await?; self.output.write_all(&[0x41, 0x45]).await?; self.output
.write_all(&[enc.strength().to_winzip_code() as u8])
.await?; self.output
.write_all(&compression_method.to_le_bytes())
.await?;
self.output.write_all(enc.salt()).await?;
self.output.write_all(enc.password_verify()).await?;
}
let encoder: Box<dyn AsyncCompressorWrite> = match self.compression_method {
CompressionMethod::Deflate => {
let level = match self.compression_level {
0 => async_compression::Level::Fastest,
1..=3 => async_compression::Level::Precise(self.compression_level as i32),
4..=6 => async_compression::Level::Default,
7..=9 => async_compression::Level::Best,
_ => async_compression::Level::Default,
};
Box::new(DeflateCompressor {
encoder: DeflateEncoder::with_quality(
CompressedBuffer::with_size_hint(size_hint),
level,
),
})
}
#[cfg(all(feature = "zstd-support", feature = "async-zstd"))]
CompressionMethod::Zstd => {
let level = async_compression::Level::Precise(self.compression_level as i32);
Box::new(ZstdCompressor {
encoder: ZstdEncoder::with_quality(
CompressedBuffer::with_size_hint(size_hint),
level,
),
})
}
#[cfg(all(feature = "zstd-support", not(feature = "async-zstd")))]
CompressionMethod::Zstd => {
return Err(SZipError::InvalidFormat(
"Zstd compression requires 'async-zstd' feature".to_string(),
));
}
CompressionMethod::Stored => Box::new(StoredCompressor {
buffer: CompressedBuffer::with_size_hint(size_hint),
}),
};
#[cfg_attr(not(feature = "encryption"), allow(unused_mut))]
let mut counter = CrcCounter::new();
#[cfg(feature = "encryption")]
if let Some(ref enc) = encryptor {
let encryption_overhead = (enc.salt().len() + 2) as u64; counter.add_compressed(encryption_overhead);
}
self.current_entry = Some(CurrentEntry {
name: name.to_string(),
local_header_offset,
encoder,
counter,
compression_method,
#[cfg(feature = "encryption")]
encryptor,
});
Ok(())
}
pub async fn write_data(&mut self, data: &[u8]) -> Result<()> {
let entry = self
.current_entry
.as_mut()
.ok_or_else(|| SZipError::InvalidFormat("No entry started".to_string()))?;
entry.counter.update_uncompressed(data);
#[cfg(feature = "encryption")]
if let Some(ref mut encryptor) = entry.encryptor {
encryptor.update_hmac(data);
}
entry.encoder.write_all(data).await?;
entry.encoder.flush().await?;
let buffer = entry.encoder.get_buffer_mut();
if buffer.should_flush() {
let compressed_data = buffer.take();
#[cfg(feature = "encryption")]
let data_to_write = if let Some(ref mut encryptor) = entry.encryptor {
let mut data_to_encrypt = compressed_data;
encryptor.encrypt(&mut data_to_encrypt)?;
data_to_encrypt
} else {
compressed_data
};
#[cfg(not(feature = "encryption"))]
let data_to_write = compressed_data;
self.output.write_all(&data_to_write).await?;
entry.counter.add_compressed(data_to_write.len() as u64);
}
Ok(())
}
async fn finish_current_entry(&mut self) -> Result<()> {
if let Some(mut entry) = self.current_entry.take() {
let mut buffer = entry.encoder.finish_compression().await?;
let remaining_data = buffer.take();
if !remaining_data.is_empty() {
#[cfg(feature = "encryption")]
let data_to_write = if let Some(ref mut encryptor) = entry.encryptor {
let mut data_to_encrypt = remaining_data;
encryptor.encrypt(&mut data_to_encrypt)?;
data_to_encrypt
} else {
remaining_data
};
#[cfg(not(feature = "encryption"))]
let data_to_write = remaining_data;
self.output.write_all(&data_to_write).await?;
entry.counter.add_compressed(data_to_write.len() as u64);
}
#[cfg(feature = "encryption")]
let (encryption_strength_code, auth_code_size) =
if let Some(encryptor) = entry.encryptor {
let strength_code = encryptor.strength().to_winzip_code();
let auth_code = encryptor.finalize();
self.output.write_all(&auth_code).await?;
(Some(strength_code), auth_code.len() as u64)
} else {
(None, 0)
};
#[cfg(not(feature = "encryption"))]
let auth_code_size = 0u64;
let crc = entry.counter.finalize();
let compressed_size = entry.counter.compressed_count + auth_code_size;
let uncompressed_size = entry.counter.uncompressed_count;
self.output.write_all(&[0x50, 0x4b, 0x07, 0x08]).await?; self.output.write_all(&crc.to_le_bytes()).await?;
if compressed_size > u32::MAX as u64 || uncompressed_size > u32::MAX as u64 {
self.output
.write_all(&compressed_size.to_le_bytes())
.await?;
self.output
.write_all(&uncompressed_size.to_le_bytes())
.await?;
} else {
self.output
.write_all(&(compressed_size as u32).to_le_bytes())
.await?;
self.output
.write_all(&(uncompressed_size as u32).to_le_bytes())
.await?;
}
self.entries.push(ZipEntry {
name: entry.name,
local_header_offset: entry.local_header_offset,
crc32: crc,
compressed_size,
uncompressed_size,
compression_method: entry.compression_method,
#[cfg(feature = "encryption")]
encryption_strength: encryption_strength_code,
});
}
Ok(())
}
pub async fn write_entries_parallel(
&mut self,
entries: Vec<crate::parallel::ParallelEntry>,
config: crate::parallel::ParallelConfig,
) -> Result<()> {
use crate::parallel::compress_entries_parallel;
self.finish_current_entry().await?;
let compressed_entries = compress_entries_parallel(entries, config).await?;
for entry in compressed_entries {
let local_header_offset = self.output.stream_position().await?;
let compressed_size = entry.data.len() as u64;
let uncompressed_size = entry.uncompressed_size;
let needs_zip64 =
compressed_size > u32::MAX as u64 || uncompressed_size > u32::MAX as u64;
self.output.write_all(&[0x50, 0x4b, 0x03, 0x04]).await?; let version_needed: u16 = if needs_zip64 { 45 } else { 20 };
self.output.write_all(&version_needed.to_le_bytes()).await?;
self.output.write_all(&[8, 0]).await?; self.output.write_all(&[8, 0]).await?; self.output.write_all(&[0, 0, 0, 0]).await?; self.output.write_all(&entry.crc32.to_le_bytes()).await?;
if needs_zip64 {
self.output.write_all(&0xFFFFFFFFu32.to_le_bytes()).await?; self.output.write_all(&0xFFFFFFFFu32.to_le_bytes()).await?; } else {
self.output
.write_all(&(compressed_size as u32).to_le_bytes())
.await?;
self.output
.write_all(&(uncompressed_size as u32).to_le_bytes())
.await?;
}
self.output
.write_all(&(entry.name.len() as u16).to_le_bytes())
.await?;
let extra_len: u16 = if needs_zip64 { 20 } else { 0 };
self.output.write_all(&extra_len.to_le_bytes()).await?;
self.output.write_all(entry.name.as_bytes()).await?;
if needs_zip64 {
self.output.write_all(&0x0001u16.to_le_bytes()).await?; self.output.write_all(&16u16.to_le_bytes()).await?; self.output
.write_all(&uncompressed_size.to_le_bytes())
.await?;
self.output
.write_all(&compressed_size.to_le_bytes())
.await?;
}
self.output.write_all(&entry.data).await?;
self.entries.push(ZipEntry {
name: entry.name,
local_header_offset,
crc32: entry.crc32,
compressed_size,
uncompressed_size,
compression_method: 8, #[cfg(feature = "encryption")]
encryption_strength: None, });
}
Ok(())
}
pub async fn finish(mut self) -> Result<W> {
self.finish_current_entry().await?;
let central_dir_offset = self.output.stream_position().await?;
for entry in &self.entries {
self.output.write_all(&[0x50, 0x4b, 0x01, 0x02]).await?; self.output.write_all(&[20, 0]).await?; self.output.write_all(&[51, 0]).await?;
#[cfg(feature = "encryption")]
let flags = if entry.encryption_strength.is_some() {
0x09 } else {
0x08 };
#[cfg(not(feature = "encryption"))]
let flags = 0x08;
self.output.write_all(&[flags, 0]).await?; self.output
.write_all(&entry.compression_method.to_le_bytes())
.await?; self.output.write_all(&[0, 0, 0, 0]).await?; self.output.write_all(&entry.crc32.to_le_bytes()).await?;
if entry.compressed_size > u32::MAX as u64 {
self.output.write_all(&0xFFFFFFFFu32.to_le_bytes()).await?;
} else {
self.output
.write_all(&(entry.compressed_size as u32).to_le_bytes())
.await?;
}
if entry.uncompressed_size > u32::MAX as u64 {
self.output.write_all(&0xFFFFFFFFu32.to_le_bytes()).await?;
} else {
self.output
.write_all(&(entry.uncompressed_size as u32).to_le_bytes())
.await?;
}
self.output
.write_all(&(entry.name.len() as u16).to_le_bytes())
.await?;
let mut extra_field: Vec<u8> = Vec::new();
#[cfg(feature = "encryption")]
if let Some(strength_code) = entry.encryption_strength {
extra_field.extend_from_slice(&[0x01, 0x99]); extra_field.extend_from_slice(&[7, 0]); extra_field.extend_from_slice(&[2, 0]); extra_field.extend_from_slice(&[0x41, 0x45]); extra_field.push(strength_code as u8); extra_field.extend_from_slice(&entry.compression_method.to_le_bytes());
}
if entry.uncompressed_size > u32::MAX as u64
|| entry.compressed_size > u32::MAX as u64
|| entry.local_header_offset > u32::MAX as u64
{
extra_field.extend_from_slice(&0x0001u16.to_le_bytes());
let mut data: Vec<u8> = Vec::new();
if entry.uncompressed_size > u32::MAX as u64 {
data.extend_from_slice(&entry.uncompressed_size.to_le_bytes());
}
if entry.compressed_size > u32::MAX as u64 {
data.extend_from_slice(&entry.compressed_size.to_le_bytes());
}
if entry.local_header_offset > u32::MAX as u64 {
data.extend_from_slice(&entry.local_header_offset.to_le_bytes());
}
extra_field.extend_from_slice(&(data.len() as u16).to_le_bytes());
extra_field.extend_from_slice(&data);
}
self.output
.write_all(&(extra_field.len() as u16).to_le_bytes())
.await?; self.output.write_all(&0u16.to_le_bytes()).await?; self.output.write_all(&0u16.to_le_bytes()).await?; self.output.write_all(&0u16.to_le_bytes()).await?; self.output.write_all(&0u32.to_le_bytes()).await?;
if entry.local_header_offset > u32::MAX as u64 {
self.output.write_all(&0xFFFFFFFFu32.to_le_bytes()).await?;
} else {
self.output
.write_all(&(entry.local_header_offset as u32).to_le_bytes())
.await?;
}
self.output.write_all(entry.name.as_bytes()).await?;
if !extra_field.is_empty() {
self.output.write_all(&extra_field).await?;
}
}
let central_dir_size = self.output.stream_position().await? - central_dir_offset;
let need_zip64 = self.entries.len() > u16::MAX as usize
|| central_dir_size > u32::MAX as u64
|| central_dir_offset > u32::MAX as u64;
if need_zip64 {
self.output.write_all(&[0x50, 0x4b, 0x06, 0x06]).await?;
let zip64_eocd_size: u64 = 44;
self.output
.write_all(&zip64_eocd_size.to_le_bytes())
.await?;
self.output.write_all(&[20, 0]).await?;
self.output.write_all(&[20, 0]).await?;
self.output.write_all(&0u32.to_le_bytes()).await?;
self.output.write_all(&0u32.to_le_bytes()).await?;
self.output
.write_all(&(self.entries.len() as u64).to_le_bytes())
.await?;
self.output
.write_all(&(self.entries.len() as u64).to_le_bytes())
.await?;
self.output
.write_all(¢ral_dir_size.to_le_bytes())
.await?;
self.output
.write_all(¢ral_dir_offset.to_le_bytes())
.await?;
self.output.write_all(&[0x50, 0x4b, 0x06, 0x07]).await?;
self.output.write_all(&0u32.to_le_bytes()).await?;
let zip64_eocd_pos = central_dir_offset + central_dir_size;
self.output.write_all(&zip64_eocd_pos.to_le_bytes()).await?;
self.output.write_all(&0u32.to_le_bytes()).await?;
}
self.output.write_all(&[0x50, 0x4b, 0x05, 0x06]).await?;
self.output.write_all(&0u16.to_le_bytes()).await?; self.output.write_all(&0u16.to_le_bytes()).await?;
if self.entries.len() > u16::MAX as usize {
self.output.write_all(&0xFFFFu16.to_le_bytes()).await?;
self.output.write_all(&0xFFFFu16.to_le_bytes()).await?;
} else {
self.output
.write_all(&(self.entries.len() as u16).to_le_bytes())
.await?;
self.output
.write_all(&(self.entries.len() as u16).to_le_bytes())
.await?;
}
if central_dir_size > u32::MAX as u64 {
self.output.write_all(&0xFFFFFFFFu32.to_le_bytes()).await?;
} else {
self.output
.write_all(&(central_dir_size as u32).to_le_bytes())
.await?;
}
if central_dir_offset > u32::MAX as u64 {
self.output.write_all(&0xFFFFFFFFu32.to_le_bytes()).await?;
} else {
self.output
.write_all(&(central_dir_offset as u32).to_le_bytes())
.await?;
}
self.output.write_all(&0u16.to_le_bytes()).await?;
self.output.flush().await?;
self.output.shutdown().await?;
Ok(self.output)
}
}