zip_core/raw/parse/
local_file_header.rs

1use super::{
2    super::{LocalFileHeader, LocalFileHeaderFixed},
3    preview_u16_from_buf, validate_length, validate_length_fixed, DynamicSizeError, FixedSizeError, Parse, ParseExtend,
4};
5
6extern crate alloc;
7use alloc::vec;
8
9impl Parse for LocalFileHeaderFixed {
10    type Error = FixedSizeError;
11
12    fn from_buf<T: bytes::Buf>(buf: &mut T) -> Result<Self, Self::Error>
13    where
14        Self: Sized,
15    {
16        let remaining = buf.remaining();
17        validate_length(remaining, Self::SIZE_IN_BYTES, FixedSizeError::UnsufficientExactBytes)?;
18        Ok(Self {
19            local_file_header_signature: buf.get_u32_le(),
20            version_needed_to_extract: buf.get_u16_le(),
21            general_purpose_bit_flag: buf.get_u16_le(),
22            compression_method: buf.get_u16_le(),
23            last_mod_file_time: buf.get_u16_le(),
24            last_mod_file_date: buf.get_u16_le(),
25            crc_32: buf.get_u32_le(),
26            compressed_size: buf.get_u32_le(),
27            uncompressed_size: buf.get_u32_le(),
28            file_name_length: buf.get_u16_le(),
29            extra_field_length: buf.get_u16_le(),
30        })
31    }
32
33    fn to_buf<T: bytes::BufMut>(&self, buf: &mut T) -> Result<(), Self::Error> {
34        let remaining = buf.remaining_mut();
35        validate_length(remaining, Self::SIZE_IN_BYTES, FixedSizeError::UnsufficientExactBytes)?;
36        buf.put_u32_le(self.local_file_header_signature);
37        buf.put_u16_le(self.version_needed_to_extract);
38        buf.put_u16_le(self.general_purpose_bit_flag);
39        buf.put_u16_le(self.compression_method);
40        buf.put_u16_le(self.last_mod_file_time);
41        buf.put_u16_le(self.last_mod_file_date);
42        buf.put_u32_le(self.crc_32);
43        buf.put_u32_le(self.compressed_size);
44        buf.put_u32_le(self.uncompressed_size);
45        buf.put_u16_le(self.file_name_length);
46        buf.put_u16_le(self.extra_field_length);
47        Ok(())
48    }
49}
50
51impl ParseExtend for LocalFileHeader {
52    type Error = DynamicSizeError;
53    type Fixed = LocalFileHeaderFixed;
54
55    fn from_buf_fixed<T: bytes::Buf>(buf: &mut T, fixed: Self::Fixed) -> Result<Self, (Self::Error, Self::Fixed)>
56    where
57        Self: Sized,
58    {
59        let total = fixed.file_name_length as usize + fixed.extra_field_length as usize;
60        let fixed = validate_length_fixed(buf.remaining(), total, fixed, DynamicSizeError::UnsufficientExactBytes)?;
61
62        let mut file_name = vec![0; fixed.file_name_length as usize];
63        let mut extra_field = vec![0; fixed.extra_field_length as usize];
64        buf.copy_to_slice(&mut file_name);
65        buf.copy_to_slice(&mut extra_field);
66
67        let s = Self {
68            fixed,
69            file_name,
70            extra_field,
71        };
72        Ok(s)
73    }
74}
75
76impl Parse for LocalFileHeader {
77    type Error = DynamicSizeError;
78
79    fn from_buf<T: bytes::Buf>(buf: &mut T) -> Result<Self, Self::Error>
80    where
81        Self: Sized,
82    {
83        let remaining = buf.remaining();
84        const SIZE: usize = LocalFileHeaderFixed::SIZE_IN_BYTES;
85        validate_length(remaining, SIZE, DynamicSizeError::UnsufficientAtLeastBytes)?;
86        const FILE_NAME_PEEK_START: usize = SIZE - 4;
87        const EXTRA_FIELD_PEEK_START: usize = SIZE - 2;
88        const ERR: DynamicSizeError = DynamicSizeError::NotContiguous(SIZE);
89        let chunk = buf.chunk();
90        let file_name_length: u16 = preview_u16_from_buf(chunk, FILE_NAME_PEEK_START).ok_or(ERR)?;
91        let extra_field_length: u16 = preview_u16_from_buf(chunk, EXTRA_FIELD_PEEK_START).ok_or(ERR)?;
92        let total = SIZE + file_name_length as usize + extra_field_length as usize;
93        validate_length(remaining, total, DynamicSizeError::UnsufficientExactBytes)?;
94
95        let fixed = LocalFileHeaderFixed::from_buf(buf).map_err(FixedSizeError::in_dynamic)?;
96        Self::from_buf_fixed(buf, fixed).map_err(|e| e.0)
97    }
98
99    fn to_buf<T: bytes::BufMut>(&self, buf: &mut T) -> Result<(), Self::Error> {
100        let remaining = buf.remaining_mut();
101        let total = LocalFileHeaderFixed::SIZE_IN_BYTES + self.file_name.len() + self.extra_field.len();
102        validate_length(remaining, total, DynamicSizeError::UnsufficientExactBytes)?;
103        self.fixed.to_buf(buf).map_err(FixedSizeError::in_dynamic)?;
104        buf.put_slice(&self.file_name);
105        buf.put_slice(&self.extra_field);
106        Ok(())
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::{super::*, *};
113
114    #[test]
115    fn cycle_local_file_header() {
116        let file_name = vec![48, 49, 32, 50];
117        let extra_field = vec![61, 62, 32, 63, 64];
118        let lf = LocalFileHeader {
119            fixed:       LocalFileHeaderFixed {
120                local_file_header_signature: LocalFileHeaderFixed::LOCAL_FILE_HEADER_SIGNATURE,
121                version_needed_to_extract: 1,
122                general_purpose_bit_flag: 2,
123                compression_method: 3,
124                last_mod_file_time: 4,
125                last_mod_file_date: 5,
126                crc_32: 6,
127                compressed_size: 7,
128                uncompressed_size: 8,
129                file_name_length: file_name.len() as u16,
130                extra_field_length: extra_field.len() as u16,
131            },
132            file_name:   file_name.clone(),
133            extra_field: extra_field.clone(),
134        };
135        let mut buf = vec![];
136        lf.to_buf(&mut buf).unwrap();
137        assert_eq!(
138            buf.len(),
139            LocalFileHeaderFixed::SIZE_IN_BYTES + file_name.len() + extra_field.len()
140        );
141        let mut readbuf = buf.as_slice();
142        let lf2 = LocalFileHeader::from_buf(&mut readbuf).unwrap();
143        assert_eq!(lf, lf2);
144        assert_eq!(lf2.file_name, file_name);
145        assert_eq!(lf2.extra_field, extra_field);
146    }
147
148    #[test]
149    fn parseextend_local_file_header() {
150        let buf: Vec<u8> = vec![48, 49, 32, 50];
151        let mut fixed = LocalFileHeaderFixed {
152            local_file_header_signature: LocalFileHeaderFixed::LOCAL_FILE_HEADER_SIGNATURE,
153            version_needed_to_extract: 1,
154            general_purpose_bit_flag: 2,
155            compression_method: 3,
156            last_mod_file_time: 4,
157            last_mod_file_date: 5,
158            crc_32: 6,
159            compressed_size: 7,
160            uncompressed_size: 8,
161            file_name_length: 3u16,
162            extra_field_length: 1u16,
163        };
164        assert!(LocalFileHeader::from_buf_fixed(&mut buf.as_slice(), fixed.clone()).is_ok());
165        fixed.extra_field_length += 1;
166        assert!(LocalFileHeader::from_buf_fixed(&mut buf.as_slice(), fixed).is_err());
167    }
168}