Skip to main content

sark_http/payload/compress/inflate/
mod.rs

1use std::{convert::Infallible, mem};
2
3use miniz_oxide::inflate::TINFLStatus;
4use o3::buffer::resident;
5
6use crate::payload::crc32::Hasher;
7
8mod checked;
9mod gzip;
10mod input;
11pub use checked::Checked;
12pub use gzip::Gzip;
13use input::Input;
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub enum Part {
17    Header,
18    Extra,
19    Name,
20    Comment,
21    HeaderChecksum,
22    Deflate,
23    Trailer,
24}
25
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
27pub enum HeaderFault {
28    Magic { actual: [u8; 2] },
29    Method { actual: u8 },
30    Flags { actual: u8 },
31    Checksum { expected: u16, actual: u16 },
32}
33
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
35pub enum Count {
36    Exact(usize),
37    Overflow,
38}
39
40impl Count {
41    const ZERO: Self = Self::Exact(0);
42
43    fn add(self, amount: usize) -> Self {
44        match self {
45            Self::Exact(current) => match current.checked_add(amount) {
46                Some(next) => Self::Exact(next),
47                None => Self::Overflow,
48            },
49            Self::Overflow => Self::Overflow,
50        }
51    }
52}
53
54#[derive(Clone, Copy, Debug, Eq, PartialEq)]
55pub enum Error {
56    Header {
57        offset: Count,
58        cause: HeaderFault,
59    },
60    Eof {
61        part: Part,
62        offset: Count,
63    },
64    Inflate {
65        offset: Count,
66        output: usize,
67        cause: TINFLStatus,
68    },
69    Checksum {
70        expected: u32,
71        actual: u32,
72    },
73    Length {
74        expected: usize,
75        actual: usize,
76    },
77    Output {
78        capacity: usize,
79        written: usize,
80    },
81    Trailing {
82        bytes: Count,
83    },
84}
85
86/// Immutable gzip members whose first header was validated before output admission.
87pub struct Plan<'a, I>
88where
89    I: Iterator<Item = &'a [u8]>,
90{
91    input: Input<'a, I>,
92    size_hint: usize,
93}
94
95impl<'a, I: Iterator<Item = &'a [u8]>> Plan<'a, I> {
96    /// Validates the header while retaining the segmented source.
97    /// `hint` is the untrusted final member size, not the combined decoded size.
98    pub fn try_segments(segments: I, hint: usize) -> Result<Self, Failure> {
99        Self::try_header::<false, Infallible>(segments, hint, None, &mut || Ok(()))
100            .map_err(Checked::into_work)
101    }
102
103    fn try_limit(self, limit: usize) -> Result<Self, DecodeError> {
104        if self.size_hint > limit {
105            return Err(DecodeError::SizeLimit {
106                actual: self.size_hint,
107                limit,
108            });
109        }
110        Ok(self)
111    }
112
113    fn try_header<const CHECK: bool, E>(
114        segments: I,
115        hint: usize,
116        checksum: Option<&Hasher>,
117        check: &mut impl FnMut() -> Result<(), E>,
118    ) -> Result<Self, Checked<Failure, E>> {
119        let mut input = Input::new(segments);
120        input
121            .header::<CHECK, E>(checksum, check)
122            .map_err(|error| error.map_work(|cause| Failure::new(cause, 0)))?;
123        Ok(Self {
124            input,
125            size_hint: hint,
126        })
127    }
128}
129
130#[derive(Clone, Copy, Debug, Eq, PartialEq)]
131pub struct Failure {
132    cause: Error,
133    written: usize,
134}
135
136#[derive(Clone, Copy, Debug, Eq, PartialEq)]
137pub enum DecodeError {
138    Stream(Failure),
139    SizeLimit {
140        actual: usize,
141        limit: usize,
142    },
143    Allocation {
144        requested: usize,
145        available: usize,
146        cause: resident::AllocError,
147    },
148}
149
150impl From<Failure> for DecodeError {
151    fn from(failure: Failure) -> Self {
152        Self::Stream(failure)
153    }
154}
155
156impl Failure {
157    const fn new(cause: Error, written: usize) -> Self {
158        Self { cause, written }
159    }
160
161    pub const fn error(&self) -> Error {
162        self.cause
163    }
164
165    pub const fn written(&self) -> usize {
166        self.written
167    }
168}
169
170const _: () = assert!(mem::size_of::<Count>() == 2 * mem::size_of::<usize>());
171const _: () = assert!(mem::size_of::<Error>() <= 5 * mem::size_of::<usize>());
172const _: () = assert!(mem::size_of::<Failure>() <= 6 * mem::size_of::<usize>());