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::Cursor;
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    segments: I,
92    input: Cursor<'a>,
93    size_hint: usize,
94}
95
96impl<'a, I: Iterator<Item = &'a [u8]>> Plan<'a, I> {
97    /// Validates the header while retaining the segmented source.
98    /// `hint` is the untrusted final member size, not the combined decoded size.
99    pub fn try_segments(segments: I, hint: usize) -> Result<Self, Failure> {
100        Self::try_header::<false, Infallible>(segments, hint, None, &mut || Ok(()))
101            .map_err(Checked::into_work)
102    }
103
104    fn try_limit(self, limit: usize) -> Result<Self, DecodeError> {
105        if self.size_hint > limit {
106            return Err(DecodeError::SizeLimit {
107                actual: self.size_hint,
108                limit,
109            });
110        }
111        Ok(self)
112    }
113
114    fn try_header<const CHECK: bool, E>(
115        mut segments: I,
116        hint: usize,
117        checksum: Option<&Hasher>,
118        check: &mut impl FnMut() -> Result<(), E>,
119    ) -> Result<Self, Checked<Failure, E>> {
120        let mut input = Cursor::new(segments.next().unwrap_or(&[]));
121        input
122            .header::<CHECK, E>(&mut segments, checksum, check)
123            .map_err(|error| error.map_work(|cause| Failure::new(cause, 0)))?;
124        Ok(Self {
125            segments,
126            input,
127            size_hint: hint,
128        })
129    }
130}
131
132#[derive(Clone, Copy, Debug, Eq, PartialEq)]
133pub struct Failure {
134    cause: Error,
135    written: usize,
136}
137
138#[derive(Clone, Copy, Debug, Eq, PartialEq)]
139pub enum DecodeError {
140    Stream(Failure),
141    SizeLimit {
142        actual: usize,
143        limit: usize,
144    },
145    Allocation {
146        requested: usize,
147        available: usize,
148        cause: resident::AllocError,
149    },
150}
151
152impl From<Failure> for DecodeError {
153    fn from(failure: Failure) -> Self {
154        Self::Stream(failure)
155    }
156}
157
158impl Failure {
159    const fn new(cause: Error, written: usize) -> Self {
160        Self { cause, written }
161    }
162
163    pub const fn error(&self) -> Error {
164        self.cause
165    }
166
167    pub const fn written(&self) -> usize {
168        self.written
169    }
170}
171
172const _: () = assert!(mem::size_of::<Count>() == 2 * mem::size_of::<usize>());
173const _: () = assert!(mem::size_of::<Error>() <= 5 * mem::size_of::<usize>());
174const _: () = assert!(mem::size_of::<Failure>() <= 6 * mem::size_of::<usize>());