use super::{
super::{LocalFileHeader, LocalFileHeaderFixed},
preview_u16_from_buf, validate_length, DynamicSizeError, FixedSizeError, Parse, ParseExtend,
};
extern crate alloc;
use alloc::vec;
impl Parse for LocalFileHeaderFixed {
type Error = FixedSizeError;
fn from_buf<T: bytes::Buf>(buf: &mut T) -> Result<Self, Self::Error>
where
Self: Sized,
{
let remaining = buf.remaining();
validate_length(remaining, Self::SIZE_IN_BYTES, FixedSizeError::UnsufficientExactBytes)?;
Ok(Self {
local_file_header_signature: buf.get_u32_le(),
version_needed_to_extract: buf.get_u16_le(),
general_purpose_bit_flag: buf.get_u16_le(),
compression_method: buf.get_u16_le(),
last_mod_file_time: buf.get_u16_le(),
last_mod_file_date: buf.get_u16_le(),
crc_32: buf.get_u32_le(),
compressed_size: buf.get_u32_le(),
uncompressed_size: buf.get_u32_le(),
file_name_length: buf.get_u16_le(),
extra_field_length: buf.get_u16_le(),
})
}
fn to_buf<T: bytes::BufMut>(&self, buf: &mut T) -> Result<(), Self::Error> {
let remaining = buf.remaining_mut();
validate_length(remaining, Self::SIZE_IN_BYTES, FixedSizeError::UnsufficientExactBytes)?;
buf.put_u32_le(self.local_file_header_signature);
buf.put_u16_le(self.version_needed_to_extract);
buf.put_u16_le(self.general_purpose_bit_flag);
buf.put_u16_le(self.compression_method);
buf.put_u16_le(self.last_mod_file_time);
buf.put_u16_le(self.last_mod_file_date);
buf.put_u32_le(self.crc_32);
buf.put_u32_le(self.compressed_size);
buf.put_u32_le(self.uncompressed_size);
buf.put_u16_le(self.file_name_length);
buf.put_u16_le(self.extra_field_length);
Ok(())
}
}
impl ParseExtend for LocalFileHeader {
type Error = DynamicSizeError;
type Fixed = LocalFileHeaderFixed;
fn from_buf_fixed<T: bytes::Buf>(buf: &mut T, fixed: Self::Fixed) -> Result<Self, (Self::Error, Self::Fixed)>
where
Self: Sized,
{
let total = fixed.file_name_length as usize + fixed.extra_field_length as usize;
if let Err(e) = validate_length(buf.remaining(), total, DynamicSizeError::UnsufficientExactBytes) {
return Err((e, fixed));
};
let mut file_name = vec![0; fixed.file_name_length as usize];
let mut extra_field = vec![0; fixed.extra_field_length as usize];
buf.copy_to_slice(&mut file_name);
buf.copy_to_slice(&mut extra_field);
Ok(Self {
fixed,
file_name,
extra_field,
})
}
}
impl Parse for LocalFileHeader {
type Error = DynamicSizeError;
fn from_buf<T: bytes::Buf>(buf: &mut T) -> Result<Self, Self::Error>
where
Self: Sized,
{
let remaining = buf.remaining();
const SIZE: usize = LocalFileHeaderFixed::SIZE_IN_BYTES;
validate_length(remaining, SIZE, DynamicSizeError::UnsufficientAtLeastBytes)?;
const FILE_NAME_PEEK_START: usize = SIZE - 4;
const EXTRA_FIELD_PEEK_START: usize = SIZE - 2;
let chunk = buf.chunk();
let file_name_length: u16 =
preview_u16_from_buf(chunk, FILE_NAME_PEEK_START).ok_or(DynamicSizeError::NotContiguous(SIZE))?;
let extra_field_length: u16 =
preview_u16_from_buf(chunk, EXTRA_FIELD_PEEK_START).ok_or(DynamicSizeError::NotContiguous(SIZE))?;
let total = SIZE + file_name_length as usize + extra_field_length as usize;
validate_length(remaining, total, DynamicSizeError::UnsufficientExactBytes)?;
let fixed = LocalFileHeaderFixed::from_buf(buf).map_err(FixedSizeError::in_dynamic)?;
Self::from_buf_fixed(buf, fixed).map_err(|e| e.0)
}
fn to_buf<T: bytes::BufMut>(&self, buf: &mut T) -> Result<(), Self::Error> {
let remaining = buf.remaining_mut();
let total = LocalFileHeaderFixed::SIZE_IN_BYTES + self.file_name.len() + self.extra_field.len();
validate_length(remaining, total, DynamicSizeError::UnsufficientExactBytes)?;
self.fixed.to_buf(buf).map_err(FixedSizeError::in_dynamic)?;
buf.put_slice(&self.file_name);
buf.put_slice(&self.extra_field);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{super::*, *};
#[test]
fn cycle_local_file_header() {
let file_name = vec![48, 49, 32, 50];
let extra_field = vec![61, 62, 32, 63, 64];
let lf = LocalFileHeader {
fixed: LocalFileHeaderFixed {
local_file_header_signature: LocalFileHeaderFixed::LOCAL_FILE_HEADER_SIGNATURE,
version_needed_to_extract: 1,
general_purpose_bit_flag: 2,
compression_method: 3,
last_mod_file_time: 4,
last_mod_file_date: 5,
crc_32: 6,
compressed_size: 7,
uncompressed_size: 8,
file_name_length: file_name.len() as u16,
extra_field_length: extra_field.len() as u16,
},
file_name: file_name.clone(),
extra_field: extra_field.clone(),
};
let mut buf = vec![];
lf.to_buf(&mut buf).unwrap();
assert_eq!(
buf.len(),
LocalFileHeaderFixed::SIZE_IN_BYTES + file_name.len() + extra_field.len()
);
let mut readbuf = buf.as_slice();
let lf2 = LocalFileHeader::from_buf(&mut readbuf).unwrap();
assert_eq!(lf, lf2);
assert_eq!(lf2.file_name, file_name);
assert_eq!(lf2.extra_field, extra_field);
}
}