1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Parsing functionality from bytes for raw types.
//! Note: this is only enabled with the `parse` feature
extern crate alloc;

mod archive_extra_data_record;
mod central_directory_header;
mod data_descriptor;
mod digital_signature;
mod end_of_central_directory;
mod extensible_data;
mod local_file_header;
mod zip64_end_of_central_directory;

#[derive(Debug)]
pub enum FixedSizeError {
    UnsufficientExactBytes(usize),
}

#[derive(Debug)]
pub enum DynamicSizeError {
    /// needed to get the size necessary before parsing. as we cannot undo the
    /// advancing on a Buf. with `std` its possible to get around this
    /// limitation by using bytes::chunks_vectored, however in no_std case we
    /// need this error type.
    NotContiguous,
    UnsufficientExactBytes(usize),
    UnsufficientAtLeastBytes(usize),
}

/// for some records, the zip convention choose to allow specifying the total
/// length in its fixed field, e.g. [`Zip64EndOfCentralDirectory`]. Those fields
/// could specify a value < its fixed size, e.g. 0 which would be totally
/// invalid and unexpected, as it would mean that we shouldn't even concider its
/// signature at this point.
///
/// [`Zip64EndOfCentralDirectory`]: super::Zip64EndOfCentralDirectory
#[derive(Debug)]
pub enum DynamicSizeTotalSizeError {
    /// needed to get the size necessary before parsing. as we cannot undo the
    /// advancing on a Buf. with `std` its possible to get around this
    /// limitation by using bytes::chunks_vectored, however in no_std case we
    /// need this error type.
    NotContiguous,
    TotalSizeInFieldIsInvalid,
    UnsufficientExactBytes(usize),
    UnsufficientAtLeastBytes(usize),
}

pub(super) fn preview_u16_from_buf(buf: &[u8], pos: usize) -> Option<u16> {
    buf.get(pos..pos + 2)
        .map(|src| unsafe { u16::from_le_bytes(*(src as *const _ as *const [_; 2])) })
}

// checks the length and returns and error in case it is not enough
pub(super) fn validate_length<E, T: Fn(usize) -> E>(have: usize, want: usize, e: T) -> Result<(), E> {
    if have < want {
        return Err(e(want - have));
    }
    Ok(())
}

pub trait Parse {
    type Error;

    /// parsing this Type from a buf, returning either the Type or an Error.
    /// implementations must not modify the buf in case of an error.
    fn from_buf<T: bytes::Buf>(buf: &mut T) -> Result<Self, Self::Error>
    where
        Self: Sized;

    /// writing self to a buffer.
    /// implementation must not modify the buf in case of an error.
    //TODO: evaluate using a different error type, because its unusual that
    // UnsufficientAtLeastBytes is thrown here.
    fn to_buf<T: bytes::BufMut>(&self, buf: &mut T) -> Result<(), Self::Error>;
}

pub trait ParseExtend {
    type Fixed;
    type Error;

    /// parsing this Type from a buf and a fixed part, returning either the Type
    /// or an Error. implementations must not modify the buf in case of an
    /// error and return the fixed
    fn from_buf_fixed<T: bytes::Buf>(buf: &mut T, fixed: Self::Fixed) -> Result<Self, (Self::Error, Self::Fixed)>
    where
        Self: Sized;
}

impl From<FixedSizeError> for DynamicSizeError {
    fn from(value: FixedSizeError) -> Self {
        match value {
            // Nature of this Error type is that there is some additional bytes, so we can only make that AtLeast
            FixedSizeError::UnsufficientExactBytes(b) => Self::UnsufficientAtLeastBytes(b),
        }
    }
}

impl From<FixedSizeError> for DynamicSizeTotalSizeError {
    fn from(value: FixedSizeError) -> Self {
        match value {
            // Nature of this Error type is that there is some additional bytes, so we can only make that AtLeast
            FixedSizeError::UnsufficientExactBytes(b) => Self::UnsufficientAtLeastBytes(b),
        }
    }
}

/// finds next occurence of a signature in a buffer, in case signature is
/// le-encoded
#[inline(always)]
#[allow(clippy::manual_find)] //performance improvement
pub fn find_next_signature(buf: &[u8], signature: [u8; 4]) -> Option<usize> {
    for i in 0..buf.len() - 4 {
        if buf[i] == signature[0]
            && buf[i + 1] == signature[1]
            && buf[i + 2] == signature[2]
            && buf[i + 3] == signature[3]
        {
            return Some(i);
        }
    }
    None
    /*
    (0..buf.len() - 4).find(|&i| {
        buf[i] == signature[0] && buf[i + 1] == signature[1] && buf[i + 2] == signature[2] && buf[i + 3] == signature[3]
    })
    */
}