zip_core/raw/parse/
mod.rs

1//! Parsing functionality from bytes for raw types.
2//! Note: this is only enabled with the `parse` feature
3extern crate alloc;
4
5mod archive_extra_data_record;
6mod central_directory_header;
7mod data_descriptor;
8mod digital_signature;
9mod end_of_central_directory;
10mod extensible_data;
11mod local_file_header;
12mod zip64_end_of_central_directory;
13
14#[cfg(feature = "std")] use std::error::Error;
15
16#[derive(Debug, PartialEq)]
17pub enum FixedSizeError {
18    /// the buffer has not enough bytes to parse this record.
19    /// An exact size is known at should be provided.
20    /// It's okay to provide a larger buffer.
21    UnsufficientExactBytes(usize),
22}
23
24#[derive(Debug, PartialEq)]
25pub enum DynamicSizeError {
26    /// needed to get the size necessary before parsing. as we cannot undo the
27    /// advancing on a Buf. with `std` its possible to get around this
28    /// limitation by using bytes::chunks_vectored, however in no_std case we
29    /// need this error type.
30    /// parameters returns how many bytes AT LEAST need to be contiguous.
31    NotContiguous(usize),
32    /// see [`FixedSizeError::UnsufficientExactBytes`]
33    ///
34    /// [`FixedSizeError::UnsufficientExactBytes`]
35    /// FixedSizeError::UnsufficientExactBytes
36    UnsufficientExactBytes(usize),
37    /// This means that the buffer has not enough bytes to read the length of
38    /// the dynamic part (e.g. there are not enough bytes to parse the fixed
39    /// part). Please catch this, and increase the buffer.
40    /// The buffer needs at least this size, hoewever it will prob fail with
41    /// a [`Self::UnsufficientExactBytes`] if you just provide `n` bytes and not
42    /// enough for the dynmapic part.
43    ///
44    /// [`Self::UnsufficientExactBytes`] Self::UnsufficientExactBytes
45    UnsufficientAtLeastBytes(usize),
46}
47
48/// for some records, the zip convention choose to allow specifying the total
49/// length in its fixed field, e.g. [`Zip64EndOfCentralDirectory`]. Those fields
50/// could specify a value < its fixed size, e.g. 0 which would be totally
51/// invalid and unexpected, as it would mean that we shouldn't even concider its
52/// signature at this point.
53///
54/// [`Zip64EndOfCentralDirectory`]: super::Zip64EndOfCentralDirectory
55#[derive(Debug, PartialEq)]
56pub enum DynamicSizeTotalSizeError {
57    Dynamic(DynamicSizeError),
58    TotalSizeInFieldIsInvalid,
59}
60
61pub(super) fn preview_u16_from_buf(buf: &[u8], pos: usize) -> Option<u16> {
62    buf.get(pos..pos + 2)
63        .map(|src| unsafe { u16::from_le_bytes(*(src as *const _ as *const [_; 2])) })
64}
65
66// checks the length and returns and error in case it is not enough
67pub(super) fn validate_length<E, T: Fn(usize) -> E>(have: usize, want: usize, e: T) -> Result<(), E> {
68    if have < want {
69        return Err(e(want - have));
70    }
71    Ok(())
72}
73
74// checks the length and returns and error in case it is not enough
75pub(super) fn validate_length_fixed<E, T: Fn(usize) -> E, T2>(
76    have: usize,
77    want: usize,
78    fixed: T2,
79    e: T,
80) -> Result<T2, (E, T2)> {
81    if have < want {
82        return Err((e(want - have), fixed));
83    }
84    Ok(fixed)
85}
86
87pub trait Parse {
88    type Error;
89
90    /// parsing this Type from a buf, returning either the Type or an Error.
91    /// implementations must not modify the buf in case of an error.
92    fn from_buf<T: bytes::Buf>(buf: &mut T) -> Result<Self, Self::Error>
93    where
94        Self: Sized;
95
96    /// writing self to a buffer.
97    /// implementation must not modify the buf in case of an error.
98    //TODO: evaluate using a different error type, because its unusual that
99    // UnsufficientAtLeastBytes is thrown here.
100    fn to_buf<T: bytes::BufMut>(&self, buf: &mut T) -> Result<(), Self::Error>;
101}
102
103pub trait ParseExtend {
104    type Fixed;
105    type Error;
106
107    /// parsing this Type from a buf and a fixed part, returning either the Type
108    /// or an Error. implementations must not modify the buf in case of an
109    /// error and return the fixed
110    fn from_buf_fixed<T: bytes::Buf>(buf: &mut T, fixed: Self::Fixed) -> Result<Self, (Self::Error, Self::Fixed)>
111    where
112        Self: Sized;
113}
114
115impl FixedSizeError {
116    pub(super) fn in_dynamic(self) -> DynamicSizeError {
117        match self {
118            // Nature of this Error type is that there is some additional bytes, so we can only make that AtLeast
119            FixedSizeError::UnsufficientExactBytes(b) => DynamicSizeError::UnsufficientAtLeastBytes(b),
120        }
121    }
122}
123
124impl From<DynamicSizeError> for DynamicSizeTotalSizeError {
125    fn from(value: DynamicSizeError) -> Self { DynamicSizeTotalSizeError::Dynamic(value) }
126}
127
128impl core::fmt::Display for FixedSizeError {
129    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
130        match self {
131            FixedSizeError::UnsufficientExactBytes(n) => write!(
132                f,
133                "Unsufficient Bytes to parse structure, the buffer needs to have {n} additional bytes to parse"
134            ),
135        }
136    }
137}
138
139impl core::fmt::Display for DynamicSizeError {
140    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
141        match self {
142            DynamicSizeError::NotContiguous(n) => write!(
143                f,
144                "Bytes support data not beeing Continous, however to peek data its required that the data is within \
145                 the first chunk of data. You need to reorganize the underlying data structure so that the first {n} \
146                 bytes are within the first chun."
147            ),
148            DynamicSizeError::UnsufficientExactBytes(n) => write!(
149                f,
150                "Unsufficient Bytes to parse structure, the buffer needs to have {n} additional bytes to parse"
151            ),
152            DynamicSizeError::UnsufficientAtLeastBytes(n) => write!(
153                f,
154                "Unsufficient Bytes to parse structure, the buffer needs to have at least {n} additional bytes to \
155                 parse, probably even more bytes are needed. An exact estimation cannot be made yet"
156            ),
157        }
158    }
159}
160
161impl core::fmt::Display for DynamicSizeTotalSizeError {
162    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
163        match self {
164            DynamicSizeTotalSizeError::Dynamic(e) => write!(f, "{e}"),
165            DynamicSizeTotalSizeError::TotalSizeInFieldIsInvalid => write!(
166                f,
167                "The Record contains an corrupt field stating the total size is lower than the minimum size"
168            ),
169        }
170    }
171}
172
173#[cfg(feature = "std")]
174impl Error for FixedSizeError {}
175#[cfg(feature = "std")]
176impl Error for DynamicSizeError {}
177#[cfg(feature = "std")]
178impl Error for DynamicSizeTotalSizeError {}
179
180/// finds next occurence of a signature in a buffer, in case signature is
181/// le-encoded
182#[inline(always)]
183#[allow(clippy::manual_find)] //performance improvement
184pub fn find_next_signature(buf: &[u8], signature: [u8; 4]) -> Option<usize> {
185    if buf.len() > 3 {
186        for i in 0..buf.len() - 3 {
187            if buf[i] == signature[0]
188                && buf[i + 1] == signature[1]
189                && buf[i + 2] == signature[2]
190                && buf[i + 3] == signature[3]
191            {
192                return Some(i);
193            }
194        }
195    }
196    None
197    /*
198    (0..buf.len() - 4).find(|&i| {
199        buf[i] == signature[0] && buf[i + 1] == signature[1] && buf[i + 2] == signature[2] && buf[i + 3] == signature[3]
200    })
201    */
202}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207
208    #[test]
209    fn find_next_signature_test() {
210        let buf = [1, 2, 3, 4, 5, 6, 7, 8];
211        assert_eq!(find_next_signature(&buf, [1, 2, 3, 4]), Some(0));
212        assert_eq!(find_next_signature(&buf, [2, 3, 4, 5]), Some(1));
213        assert_eq!(find_next_signature(&buf, [3, 4, 5, 6]), Some(2));
214        assert_eq!(find_next_signature(&buf, [4, 3, 2, 1]), None);
215        assert_eq!(find_next_signature(&buf, [0, 1, 2, 3]), None);
216        assert_eq!(find_next_signature(&buf, [6, 7, 8, 9]), None);
217        assert_eq!(find_next_signature(&buf, [5, 6, 7, 8]), Some(4));
218
219        let buf = [1, 2, 3];
220        assert_eq!(find_next_signature(&buf, [1, 2, 3, 4]), None);
221
222        let buf = [1, 2];
223        assert_eq!(find_next_signature(&buf, [1, 2, 3, 4]), None);
224
225        let buf = [];
226        assert_eq!(find_next_signature(&buf, [1, 2, 3, 4]), None);
227    }
228
229    #[test]
230    fn display_dont_panic() {
231        let mut buf = vec![0; 20000];
232        use std::io::Write;
233        write!(buf, "{}", FixedSizeError::UnsufficientExactBytes(1)).unwrap();
234        write!(buf, "{}", DynamicSizeError::NotContiguous(2)).unwrap();
235        write!(buf, "{}", DynamicSizeError::UnsufficientExactBytes(3)).unwrap();
236        write!(buf, "{}", DynamicSizeError::UnsufficientAtLeastBytes(4)).unwrap();
237        write!(
238            buf,
239            "{}",
240            DynamicSizeTotalSizeError::Dynamic(DynamicSizeError::NotContiguous(5))
241        )
242        .unwrap();
243        write!(buf, "{}", DynamicSizeTotalSizeError::TotalSizeInFieldIsInvalid).unwrap();
244    }
245}