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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! RAII wrapper around zlib-rs's raw-inflate ABI.
//!
//! Every decode path in this crate drives inflate through this wrapper, which
//! owns exactly one initialized `z_stream`, ends it exactly once, and confines
//! the crate's inflate-side `unsafe` to one place. Resuming mid-stream uses
//! [`RawInflater::prime`] for a bit offset that is not byte aligned and
//! [`RawInflater::set_dictionary_bytes`] for the predecessor window.
use crate::{DecodeError, DeflateErrorKind};
use libz_rs_sys as z;
use std::ffi::CStr;
use std::mem::size_of;
/// RAII wrapper around zlib-rs's zlib-compatible raw-inflate ABI.
pub(crate) struct RawInflater {
pub(crate) stream: z::z_stream,
initialized: bool,
window_bits: u8,
}
impl RawInflater {
pub(crate) fn new() -> Result<Self, DecodeError> {
Self::new_with_window_bits(15)
}
/// Initializes raw inflation with an RFC 1950 CINFO-derived window.
pub(crate) fn new_with_window_bits(window_bits: u8) -> Result<Self, DecodeError> {
if !(8..=15).contains(&window_bits) {
return Err(DecodeError::InvalidDeflate {
bit_offset: 0,
reason: DeflateErrorKind::InvalidData,
});
}
let mut result = Self {
stream: z::z_stream::default(),
initialized: false,
window_bits,
};
// SAFETY:
// - `result.stream` is a live, uniquely borrowed `z_stream`.
// - `zlibVersion` returns a static NUL-terminated version string.
// - the structure size matches the exact Rust ABI type passed.
// - a negative `window_bits` requests raw DEFLATE with the container's
// declared history limit.
let status = unsafe {
z::inflateInit2_(
&mut result.stream,
-i32::from(window_bits),
z::zlibVersion(),
size_of::<z::z_stream>() as i32,
)
};
if status != z::Z_OK {
return Err(DecodeError::InvalidDeflate {
bit_offset: 0,
reason: DeflateErrorKind::BackendStatus(status),
});
}
result.initialized = true;
Ok(result)
}
pub(crate) fn message(&self) -> Option<String> {
if self.stream.msg.is_null() {
return None;
}
// SAFETY: while the initialized zlib stream is live, zlib owns `msg`
// as a valid NUL-terminated diagnostic string or leaves it null.
Some(
unsafe { CStr::from_ptr(self.stream.msg) }
.to_string_lossy()
.into_owned(),
)
}
pub(crate) fn reset(&mut self, bit_offset: u64) -> Result<(), DecodeError> {
self.reset_with_window_bits(self.window_bits, bit_offset)
}
/// Resets raw inflation and changes the maximum history window.
pub(crate) fn reset_with_window_bits(
&mut self,
window_bits: u8,
bit_offset: u64,
) -> Result<(), DecodeError> {
if !(8..=15).contains(&window_bits) {
return Err(DecodeError::InvalidDeflate {
bit_offset,
reason: DeflateErrorKind::InvalidData,
});
}
// SAFETY: this wrapper owns a successfully initialized stream and
// holds its unique mutable borrow. A negative value keeps raw mode;
// the accepted range is checked above.
let status = unsafe { z::inflateReset2(&mut self.stream, -i32::from(window_bits)) };
if status == z::Z_OK {
self.window_bits = window_bits;
Ok(())
} else {
Err(DecodeError::InvalidDeflate {
bit_offset,
reason: DeflateErrorKind::BackendStatus(status),
})
}
}
pub(crate) fn prime(
&mut self,
bits: u8,
value: u8,
bit_offset: u64,
) -> Result<(), DecodeError> {
// SAFETY: the stream is initialized and uniquely borrowed. zlib accepts
// at most 16 low-order bits before the first inflate call; this wrapper
// supplies at most the seven unread bits from one source byte.
let status =
unsafe { z::inflatePrime(&mut self.stream, i32::from(bits), i32::from(value)) };
if status == z::Z_OK {
Ok(())
} else {
Err(DecodeError::InvalidDeflate {
bit_offset,
reason: DeflateErrorKind::BackendStatus(status),
})
}
}
/// Installs `bytes` as the raw-inflate history.
///
/// The indexed reader supplies stored windows, while the parallel decoder
/// supplies only the suffix permitted by the selected container.
pub(crate) fn set_dictionary_bytes(
&mut self,
bytes: &[u8],
bit_offset: u64,
) -> Result<(), DecodeError> {
if bytes.is_empty() {
return Ok(());
}
if bytes.len() > 32768 {
return Err(DecodeError::InvalidDeflate {
bit_offset,
reason: DeflateErrorKind::InvalidData,
});
}
// SAFETY: `bytes` remains immutably borrowed for the call, and its
// length is checked against DEFLATE's 32 KiB history limit above.
let status = unsafe {
z::inflateSetDictionary(&mut self.stream, bytes.as_ptr(), bytes.len() as u32)
};
if status == z::Z_OK {
Ok(())
} else {
Err(DecodeError::InvalidDeflate {
bit_offset,
reason: DeflateErrorKind::BackendStatus(status),
})
}
}
}
impl Drop for RawInflater {
fn drop(&mut self) {
if self.initialized {
// SAFETY: this wrapper calls `inflateEnd` exactly once for the
// successfully initialized, uniquely owned stream.
let _ = unsafe { z::inflateEnd(&mut self.stream) };
}
}
}