1pub mod futures;
2pub mod tokio;
3
4#[derive(Debug)]
30pub struct ZstdSeekTable {
31 frames: Vec<ZstdFrame>,
32}
33
34impl ZstdSeekTable {
35 pub(crate) fn empty() -> Self {
36 Self { frames: vec![] }
37 }
38
39 pub fn num_frames(&self) -> usize {
41 self.frames.len()
42 }
43
44 pub fn frames(&self) -> impl Iterator<Item = ZstdFrame> + '_ {
47 self.frames.iter().copied()
48 }
49
50 pub(crate) fn first_frame(&self) -> Option<ZstdFrame> {
53 self.frames.first().copied()
54 }
55
56 pub(crate) fn last_frame(&self) -> Option<ZstdFrame> {
59 self.frames.last().copied()
60 }
61
62 pub(crate) fn find_by_decompressed_pos(&self, pos: u64) -> Option<ZstdFrame> {
63 let index = self
64 .frames
65 .binary_search_by(|frame| {
66 if pos < frame.decompressed_pos {
67 std::cmp::Ordering::Greater
68 } else if pos >= frame.decompressed_pos + frame.size.decompressed_size {
69 std::cmp::Ordering::Less
70 } else {
71 std::cmp::Ordering::Equal
72 }
73 })
74 .ok()?;
75 let frame = self.frames[index];
76 Some(frame)
77 }
78
79 pub(crate) fn get(&self, index: usize) -> Option<ZstdFrame> {
80 self.frames.get(index).copied()
81 }
82
83 pub(crate) fn insert(&mut self, frame: ZstdFrame) {
84 let next_index = self.frames.len();
85
86 assert!(next_index >= frame.index);
87
88 if frame.index == next_index {
89 self.frames.push(frame);
90 } else if frame.index + 1 == next_index {
91 self.frames[frame.index] = frame;
92 }
93 }
94}
95
96#[derive(Debug, Clone, Copy)]
100pub struct ZstdFrame {
101 pub(crate) index: usize,
102 pub(crate) compressed_pos: u64,
103 pub(crate) decompressed_pos: u64,
104 pub(crate) size: ZstdFrameSize,
105}
106
107impl ZstdFrame {
108 pub(crate) fn compressed_end(&self) -> u64 {
109 self.compressed_pos + self.size.compressed_size
110 }
111
112 pub(crate) fn decompressed_end(&self) -> u64 {
113 self.decompressed_pos + self.size.decompressed_size
114 }
115
116 pub fn compressed_size(&self) -> u64 {
118 self.size.compressed_size
119 }
120
121 pub fn decompressed_size(&self) -> u64 {
123 self.size.decompressed_size
124 }
125
126 pub fn compressed_range(&self) -> std::ops::Range<u64> {
129 self.compressed_pos..self.compressed_end()
130 }
131
132 pub fn decompressed_range(&self) -> std::ops::Range<u64> {
135 self.decompressed_pos..self.decompressed_end()
136 }
137}
138
139#[derive(Debug, Default, Clone, Copy)]
140pub(crate) struct ZstdFrameSize {
141 pub(crate) compressed_size: u64,
142 pub(crate) decompressed_size: u64,
143}
144
145impl ZstdFrameSize {
146 pub(crate) fn add_sizes(&mut self, compressed_size: usize, decompressed_size: usize) {
147 let compressed_written: u64 = compressed_size
148 .try_into()
149 .expect("failed to convert written bytes to u64");
150 let decompressed_written: u64 = decompressed_size
151 .try_into()
152 .expect("failed to convert written bytes to u64");
153
154 let compressed_size = self
155 .compressed_size
156 .checked_add(compressed_written)
157 .expect("adding to compressed size overflowed");
158 let decompressed_size = self
159 .decompressed_size
160 .checked_add(decompressed_written)
161 .expect("adding to decompressed size overflowed");
162
163 self.compressed_size = compressed_size;
164 self.decompressed_size = decompressed_size;
165 }
166}
167
168pub fn read_seek_table<R>(reader: &mut R) -> std::io::Result<Option<ZstdSeekTable>>
189where
190 R: std::io::Read + std::io::Seek,
191{
192 let initial_position = reader.stream_position()?;
194
195 let seek_table_result = read_seek_table_inner(reader);
197
198 let seek_result = reader.seek(std::io::SeekFrom::Start(initial_position));
201
202 let seek_table = seek_table_result?;
204 seek_result?;
205
206 Ok(seek_table)
207}
208
209fn read_seek_table_inner<R>(reader: &mut R) -> std::io::Result<Option<ZstdSeekTable>>
210where
211 R: std::io::Read + std::io::Seek,
212{
213 reader.seek(std::io::SeekFrom::End(-9))?;
215
216 let mut num_frames_bytes = [0; 4];
219 reader.read_exact(&mut num_frames_bytes)?;
220
221 let mut seek_table_descriptor_bytes = [0; 1];
222 reader.read_exact(&mut seek_table_descriptor_bytes)?;
223
224 let mut seekable_magic_number_bytes = [0; 4];
225 reader.read_exact(&mut seekable_magic_number_bytes)?;
226
227 if seekable_magic_number_bytes != crate::SEEKABLE_FOOTER_MAGIC_BYTES {
229 return Ok(None);
230 }
231
232 let num_frames = u32::from_le_bytes(num_frames_bytes);
234
235 let [seek_table_descriptor] = seek_table_descriptor_bytes;
237 let has_checksum = seek_table_descriptor & 0b1000_0000 != 0;
238 let is_reserved_valid = seek_table_descriptor & 0b0111_1100 == 0;
239
240 if !is_reserved_valid {
241 return Err(std::io::Error::other(
242 "zstd seek table has unsupported descriptor",
243 ));
244 }
245
246 let table_entry_size: u32 = if has_checksum { 12 } else { 8 };
248
249 let table_frame_size = table_entry_size
253 .checked_mul(num_frames)
254 .and_then(|size| size.checked_add(9))
255 .ok_or_else(|| std::io::Error::other("zstd seek table size overflowed"))?;
256
257 reader.seek_relative(-i64::from(table_frame_size) - 8)?;
259
260 let mut skippable_magic_number_bytes = [0; 4];
263 reader.read_exact(&mut skippable_magic_number_bytes)?;
264
265 let mut actual_table_frame_size_bytes = [0; 4];
266 reader.read_exact(&mut actual_table_frame_size_bytes)?;
267
268 if skippable_magic_number_bytes != crate::SKIPPABLE_HEADER_MAGIC_BYTES {
270 return Err(std::io::Error::other(
271 "zstd seek table has unsupported skippable frame magic number",
272 ));
273 }
274
275 let actual_table_frame_size = u32::from_le_bytes(actual_table_frame_size_bytes);
276 if actual_table_frame_size != table_frame_size {
277 return Err(std::io::Error::other("zstd seek table size did not match"));
278 }
279
280 let mut table = ZstdSeekTable::empty();
282 let mut compressed_pos = 0;
283 let mut decompressed_pos = 0;
284 for frame_index in 0..num_frames {
285 let frame_index = usize::try_from(frame_index).unwrap();
286
287 let mut compressed_size_bytes = [0; 4];
289 reader.read_exact(&mut compressed_size_bytes)?;
290 let compressed_size = u32::from_le_bytes(compressed_size_bytes);
291
292 let mut decompressed_size_bytes = [0; 4];
294 reader.read_exact(&mut decompressed_size_bytes)?;
295 let decompressed_size = u32::from_le_bytes(decompressed_size_bytes);
296
297 if has_checksum {
299 reader.seek_relative(4)?;
300 }
301
302 let frame = ZstdFrame {
303 compressed_pos,
304 decompressed_pos,
305 index: frame_index,
306 size: ZstdFrameSize {
307 compressed_size: compressed_size.into(),
308 decompressed_size: decompressed_size.into(),
309 },
310 };
311 table.insert(frame);
312
313 compressed_pos += u64::from(compressed_size);
314 decompressed_pos += u64::from(decompressed_size);
315 }
316
317 Ok(Some(table))
318}