use super::{
super::{CentralDirectoryHeader, CentralDirectoryHeaderFixed},
preview_u16_from_buf, validate_length, validate_length_fixed, DynamicSizeError, FixedSizeError, Parse, ParseExtend,
};
extern crate alloc;
use alloc::vec;
impl Parse for CentralDirectoryHeaderFixed {
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 {
central_file_header_signature: buf.get_u32_le(),
version_made_by: buf.get_u16_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(),
file_comment_length: buf.get_u16_le(),
disk_number_start: buf.get_u16_le(),
internal_file_attributes: buf.get_u16_le(),
external_file_attributes: buf.get_u32_le(),
relative_offset_of_local_header: buf.get_u32_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.central_file_header_signature);
buf.put_u16_le(self.version_made_by);
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);
buf.put_u16_le(self.file_comment_length);
buf.put_u16_le(self.disk_number_start);
buf.put_u16_le(self.internal_file_attributes);
buf.put_u32_le(self.external_file_attributes);
buf.put_u32_le(self.relative_offset_of_local_header);
Ok(())
}
}
impl ParseExtend for CentralDirectoryHeader {
type Error = DynamicSizeError;
type Fixed = CentralDirectoryHeaderFixed;
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;
let total = total + fixed.file_comment_length as usize;
let fixed = validate_length_fixed(buf.remaining(), total, fixed, DynamicSizeError::UnsufficientExactBytes)?;
let mut file_name = vec![0; fixed.file_name_length as usize];
let mut extra_field = vec![0; fixed.extra_field_length as usize];
let mut file_comment = vec![0; fixed.file_comment_length as usize];
buf.copy_to_slice(&mut file_name);
buf.copy_to_slice(&mut extra_field);
buf.copy_to_slice(&mut file_comment);
let s = Self {
fixed,
file_name,
extra_field,
file_comment,
};
Ok(s)
}
}
impl Parse for CentralDirectoryHeader {
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 = CentralDirectoryHeaderFixed::SIZE_IN_BYTES;
validate_length(remaining, SIZE, DynamicSizeError::UnsufficientAtLeastBytes)?;
const FILE_NAME_PEEK_START: usize = 28;
const EXTRA_FIELD_PEEK_START: usize = 30;
const FILE_COMMENT_PEEK_START: usize = 32;
let chunk = buf.chunk();
const PEEK_END: DynamicSizeError = DynamicSizeError::NotContiguous(FILE_COMMENT_PEEK_START + 2);
let file_name_length: u16 = preview_u16_from_buf(chunk, FILE_NAME_PEEK_START).ok_or(PEEK_END)?;
let extra_field_length: u16 = preview_u16_from_buf(chunk, EXTRA_FIELD_PEEK_START).ok_or(PEEK_END)?;
let file_comment_length: u16 = preview_u16_from_buf(chunk, FILE_COMMENT_PEEK_START).ok_or(PEEK_END)?;
let total = SIZE + file_name_length as usize + extra_field_length as usize + file_comment_length as usize;
validate_length(remaining, total, DynamicSizeError::UnsufficientExactBytes)?;
let fixed = CentralDirectoryHeaderFixed::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();
const SIZE: usize = CentralDirectoryHeaderFixed::SIZE_IN_BYTES;
let total = SIZE + self.file_name.len() + self.extra_field.len() + self.file_comment.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);
buf.put_slice(&self.file_comment);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{super::*, *};
use crate::raw::parse::DynamicSizeError;
use bytes::{Buf, Bytes};
#[test]
fn cycle_central_directory_header() {
let file_name = vec![48, 49, 32, 50];
let extra_field = vec![61, 62, 32, 63, 64];
let file_comment = vec![51, 52, 53, 32, 54, 55];
let cd = CentralDirectoryHeader {
fixed: CentralDirectoryHeaderFixed {
central_file_header_signature: 1,
version_made_by: 2,
version_needed_to_extract: 3,
general_purpose_bit_flag: 4,
compression_method: 5,
last_mod_file_time: 6,
last_mod_file_date: 7,
crc_32: 8,
compressed_size: 9,
uncompressed_size: 10,
file_name_length: file_name.len() as u16,
extra_field_length: extra_field.len() as u16,
file_comment_length: file_comment.len() as u16,
disk_number_start: 11,
internal_file_attributes: 12,
external_file_attributes: 13,
relative_offset_of_local_header: 14,
},
file_name: file_name.clone(),
extra_field: extra_field.clone(),
file_comment: file_comment.clone(),
};
let mut buf = vec![];
cd.to_buf(&mut buf).unwrap();
assert_eq!(
buf.len(),
CentralDirectoryHeaderFixed::SIZE_IN_BYTES + file_name.len() + extra_field.len() + file_comment.len()
);
let mut readbuf = buf.as_slice();
let cd2 = CentralDirectoryHeader::from_buf(&mut readbuf).unwrap();
assert_eq!(cd, cd2);
assert_eq!(cd2.file_name, file_name);
assert_eq!(cd2.extra_field, extra_field);
assert_eq!(cd2.file_comment, file_comment);
}
const VALID_CENTRAL_DIRECTORY_HEADER: [u8; 117] = [
80, 75, 1, 2, 20, 3, 20, 0, 8, 8, 8, 0, 172, 144, 201, 88, 196, 141, 233, 151, 133, 0, 0, 0, 211, 0, 0, 0, 38,
0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 182, 129, 34, 121, 0, 0, 97, 115, 115, 101, 116, 115, 47, 99, 111, 109, 109,
111, 110, 47, 97, 98, 105, 108, 105, 116, 105, 101, 115, 47, 97, 120, 101, 47, 115, 117, 110, 100, 101, 114,
46, 114, 111, 110, 117, 120, 11, 0, 1, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 85, 84, 5, 0, 1, 229, 238, 101, 102, 85,
84, 5, 0, 1, 229, 238, 101, 102,
];
#[test]
fn parse_from_buf() {
let mut buf = Bytes::copy_from_slice(&VALID_CENTRAL_DIRECTORY_HEADER);
let cd = CentralDirectoryHeader::from_buf(&mut buf).unwrap();
assert_eq!(
Ok("assets/common/abilities/axe/sunder.ron"),
std::str::from_utf8(&cd.file_name)
);
}
#[test]
fn parse_not_enough_bytes() {
let mut modified = Vec::from(VALID_CENTRAL_DIRECTORY_HEADER);
assert_eq!(38, modified[28]);
modified[28] = 39;
let mut buf = Bytes::copy_from_slice(&modified);
assert_eq!(
Err(DynamicSizeError::UnsufficientExactBytes(1)),
CentralDirectoryHeader::from_buf(&mut buf)
);
assert_eq!(VALID_CENTRAL_DIRECTORY_HEADER.len(), buf.remaining());
assert_eq!(80, buf.get_u8());
}
#[test]
fn parse_enough_bytes() {
let mut modified = Vec::from(VALID_CENTRAL_DIRECTORY_HEADER);
assert_eq!(38, modified[28]);
modified[28] = 37;
let mut buf = Bytes::copy_from_slice(&modified);
let cd = CentralDirectoryHeader::from_buf(&mut buf).unwrap();
assert_eq!(
Ok("assets/common/abilities/axe/sunder.ro"),
std::str::from_utf8(&cd.file_name)
);
assert_eq!(1, buf.remaining());
assert_eq!(102, buf.get_u8());
}
#[test]
fn parse_non_continougs() {
let original1 = Vec::from(&VALID_CENTRAL_DIRECTORY_HEADER[0..33]);
let original2 = Vec::from(&VALID_CENTRAL_DIRECTORY_HEADER[33..VALID_CENTRAL_DIRECTORY_HEADER.len()]);
let mut chained_buf = Bytes::copy_from_slice(&original1).chain(Bytes::copy_from_slice(&original2));
assert_eq!(
Err(DynamicSizeError::NotContiguous(34)),
CentralDirectoryHeader::from_buf(&mut chained_buf)
);
}
#[test]
fn parse_continougs() {
let original1 = Vec::from(&VALID_CENTRAL_DIRECTORY_HEADER[0..34]);
let original2 = Vec::from(&VALID_CENTRAL_DIRECTORY_HEADER[34..VALID_CENTRAL_DIRECTORY_HEADER.len()]);
let mut chained_buf = Bytes::copy_from_slice(&original1).chain(Bytes::copy_from_slice(&original2));
let _cd = CentralDirectoryHeader::from_buf(&mut chained_buf).unwrap();
}
#[test]
fn from_buf_fixed_parse() {
let mut buf = Bytes::copy_from_slice(&VALID_CENTRAL_DIRECTORY_HEADER);
let cd_fixed = CentralDirectoryHeaderFixed::from_buf(&mut buf).unwrap();
let _cd = CentralDirectoryHeader::from_buf_fixed(&mut buf, cd_fixed).unwrap();
}
#[test]
fn from_buf_fixed_invalid_size() {
let mut buf = Bytes::copy_from_slice(&VALID_CENTRAL_DIRECTORY_HEADER);
let mut cd_fixed = CentralDirectoryHeaderFixed::from_buf(&mut buf).unwrap();
cd_fixed.file_name_length += 1;
let cd_fixed2 = cd_fixed.clone();
assert_eq!(
Err((DynamicSizeError::UnsufficientExactBytes(1), cd_fixed)),
CentralDirectoryHeader::from_buf_fixed(&mut buf, cd_fixed2)
);
}
}