Skip to content

Commit

Permalink
Introduce tracing, add util functions, add core raw macro, add core l…
Browse files Browse the repository at this point in the history
…fh logic
  • Loading branch information
Majored committed Oct 27, 2024
1 parent 60b8d12 commit f60a9d5
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 10 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ crc32fast = "1"
futures-lite = { version = "2.1.0", default-features = false, features = ["std"] }
pin-project = "1"
thiserror = "1"
tracing = "0.1.40"

async-compression = { version = "0.4.2", default-features = false, features = ["futures-io"], optional = true }
chrono = { version = "0.4", default-features = false, features = ["clock"], optional = true }
Expand Down
67 changes: 67 additions & 0 deletions src/core/lfh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) 2024 Harry [Majored] [hello@majored.pw]
// MIT License (https://github.com/Majored/rs-async-zip/blob/main/LICENSE)

use crate::core::raw;
use futures_lite::io::AsyncWriteExt;

pub const SIGNATURE: u32 = 0x4034b50;

raw! {
RawLocalFileHeader {
version_needed_to_extract, u16, crate::utils::read_u16, crate::utils::write_u16,
general_purpose_flags, u16, crate::utils::read_u16, crate::utils::write_u16,
compression_method, u16, crate::utils::read_u16, crate::utils::write_u16,
last_mod_file_time, u16, crate::utils::read_u16, crate::utils::write_u16,
last_mod_file_date, u16, crate::utils::read_u16, crate::utils::write_u16,
crc_32, u32, crate::utils::read_u32, crate::utils::write_u32,
compressed_size, u32, crate::utils::read_u32, crate::utils::write_u32,
uncompressed_size, u32, crate::utils::read_u32, crate::utils::write_u32,
file_name_length, u16, crate::utils::read_u16, crate::utils::write_u16,
extra_field_length, u16, crate::utils::read_u16, crate::utils::write_u16
}
}

pub struct LocalFileHeader {
pub raw: RawLocalFileHeader,
pub file_name: Vec<u8>,
pub extra_field: Vec<u8>,
}

/// Reads a local file header from the given reader.
///
/// This function does so by:
/// - asserting the signature of the local file header
/// - reading the raw local file header
/// - reading the file name
/// - reading the extra field
#[tracing::instrument(skip(reader))]
pub async fn read(mut reader: impl AsyncRead + Unpin) -> Result<LocalFileHeader> {
crate::utils::assert_signature(&mut reader, SIGNATURE).await?;

let raw = raw_read(&mut reader).await?;
let file_name = crate::utils::read_bytes(&mut reader, raw.file_name_length as usize).await?;
let extra_field = crate::utils::read_bytes(&mut reader, raw.extra_field_length as usize).await?;

Ok(LocalFileHeader {
raw,
file_name,
extra_field,
})
}

/// Writes a local file header to the given writer.
///
/// This function does so by:
/// - writing the signature of the local file header
/// - writing the raw local file header
/// - writing the file name
/// - writing the extra field
pub async fn write(mut writer: impl AsyncWrite + Unpin, header: &LocalFileHeader) -> Result<()> {
crate::utils::write_u32(&mut writer, SIGNATURE).await?;

raw_write(&mut writer, &header.raw).await?;
writer.write_all(&header.file_name).await?;
writer.write_all(&header.extra_field).await?;

Ok(())
}
33 changes: 33 additions & 0 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2024 Harry [Majored] [hello@majored.pw]
// MIT License (https://github.com/Majored/rs-async-zip/blob/main/LICENSE)

pub mod lfh;
pub mod cdr;

macro_rules! raw {
($name:ident { $($field:ident, $type:ty, $read:expr, $write:expr),* }) => {
use crate::error::Result;
use futures_lite::io::{AsyncRead, AsyncWrite};

pub struct $name {
$(pub $field : $type),*
}

/// Reads the raw underlying header from the given reader.
#[tracing::instrument(skip(reader))]
pub async fn raw_read(mut reader: impl AsyncRead + Unpin) -> Result<$name> {
Ok($name {
$($field : $read(&mut reader).await? ),*
})
}

/// Writes the raw underlying header to the given writer.
#[tracing::instrument(skip(writer, raw))]
pub async fn raw_write(mut writer: impl AsyncWrite + Unpin, raw: &$name) -> Result<()> {
$($write(&mut writer, raw.$field).await?;)*
Ok(())
}
}
}

pub(crate) use raw;
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
pub mod base;
pub mod error;
pub mod core;

#[cfg(feature = "tokio")]
pub mod tokio;
Expand Down
53 changes: 43 additions & 10 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,50 @@
// MIT License (https://github.com/Majored/rs-async-zip/blob/main/LICENSE)

use crate::error::{Result, ZipError};
use futures_lite::io::{AsyncRead, AsyncReadExt};
use futures_lite::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};

// Assert that the next four-byte signature read by a reader which impls AsyncRead matches the expected signature.
pub(crate) async fn assert_signature<R: AsyncRead + Unpin>(reader: &mut R, expected: u32) -> Result<()> {
let signature = {
let mut buffer = [0; 4];
reader.read_exact(&mut buffer).await?;
u32::from_le_bytes(buffer)
};
match signature {
actual if actual == expected => Ok(()),
actual => Err(ZipError::UnexpectedHeaderError(actual, expected)),
#[tracing::instrument(skip(reader))]
pub(crate) async fn assert_signature(reader: impl AsyncRead + Unpin, expected: u32) -> Result<()> {
let actual = read_u32(reader).await?;

if actual != expected {
return Err(ZipError::UnexpectedHeaderError(actual, expected));
}

Ok(())
}

#[tracing::instrument(skip(reader))]
pub(crate) async fn read_u16(mut reader: impl AsyncRead + Unpin) -> Result<u16> {
let mut buf = [0u8; 2];
reader.read_exact(&mut buf).await.unwrap();
Ok(u16::from_le_bytes(buf))
}

#[tracing::instrument(skip(reader))]
pub(crate) async fn read_u32(mut reader: impl AsyncRead + Unpin) -> Result<u32> {
let mut buf = [0u8; 4];
reader.read_exact(&mut buf).await.unwrap();
Ok(u32::from_le_bytes(buf))
}

#[tracing::instrument(skip(writer))]
pub(crate) async fn write_u16(mut writer: impl AsyncWrite + Unpin, value: u16) -> Result<()> {
writer.write_all(&value.to_be_bytes()).await?;
Ok(())
}

#[tracing::instrument(skip(writer))]
pub(crate) async fn write_u32(mut writer: impl AsyncWrite + Unpin, value: u32) -> Result<()> {
writer.write_all(&value.to_be_bytes()).await?;
Ok(())
}

/// Read and return a dynamic length vector of bytes from a reader which impls AsyncRead.
#[tracing::instrument(skip(reader))]
pub(crate) async fn read_bytes(reader: impl AsyncRead + Unpin, length: usize) -> Result<Vec<u8>> {
let mut buffer = Vec::with_capacity(length);
reader.take(length as u64).read_to_end(&mut buffer).await?;
Ok(buffer)
}

0 comments on commit f60a9d5

Please sign in to comment.