sevenz_turbo/reader/
positional_block.rs1use super::*;
2
3pub struct PositionalBlockDecoder {
16 archive: Arc<Archive>,
17 password: Arc<Password>,
18 source: Arc<dyn ReadAt>,
19 block_index: usize,
20 limits: ArchiveLimits,
21 thread_count: u32,
22 adaptive_lzma2: bool,
23 verify_checksums: bool,
24 lzma2: Arc<Lzma2Control>,
25 on_sub_stream_complete: Option<Box<dyn FnMut(SubStreamCompletion) + Send>>,
26}
27
28impl PositionalBlockDecoder {
29 pub fn new(
39 archive: Arc<Archive>,
40 password: Arc<Password>,
41 source: Arc<dyn ReadAt>,
42 block_index: usize,
43 ) -> Result<Self, Error> {
44 Self::with_limits(
45 archive,
46 password,
47 source,
48 block_index,
49 ArchiveLimits::default(),
50 )
51 }
52
53 pub fn with_limits(
60 archive: Arc<Archive>,
61 password: Arc<Password>,
62 source: Arc<dyn ReadAt>,
63 block_index: usize,
64 limits: ArchiveLimits,
65 ) -> Result<Self, Error> {
66 if block_index >= archive.blocks.len() {
67 return Err(Error::invalid_argument("block index out of range"));
68 }
69 let start = *archive
70 .stream_map
71 .block_first_file_index
72 .get(block_index)
73 .ok_or_else(|| Error::corrupted("missing block file mapping"))?;
74 let count = archive.blocks[block_index].num_unpack_sub_streams;
75 let entries = start
76 .checked_add(count)
77 .and_then(|end| archive.files.get(start..end))
78 .ok_or_else(|| Error::corrupted("invalid block file mapping"))?;
79 if entries.iter().enumerate().any(|(offset, entry)| {
80 !entry.has_stream
81 || archive.stream_map.file_block_index.get(start + offset)
82 != Some(&Some(block_index))
83 }) {
84 return Err(Error::unsupported(
85 "positional block walk requires contiguous data members",
86 ));
87 }
88 Ok(Self {
89 archive,
90 password,
91 source,
92 block_index,
93 limits,
94 thread_count: 1,
95 adaptive_lzma2: false,
96 verify_checksums: true,
97 lzma2: Arc::new(Lzma2Control::new(1)),
98 on_sub_stream_complete: None,
99 })
100 }
101
102 pub fn set_threads(&mut self, threads: u32) {
104 self.thread_count = threads.clamp(1, 256);
105 self.lzma2.set_threads(self.thread_count);
106 }
107
108 #[must_use]
110 pub fn with_threads(mut self, threads: u32) -> Self {
111 self.set_threads(threads);
112 self
113 }
114
115 #[must_use]
117 pub fn with_adaptive_lzma2(mut self) -> Self {
118 self.adaptive_lzma2 = true;
119 self
120 }
121
122 #[must_use]
124 pub fn with_verify_checksums(mut self, verify: bool) -> Self {
125 self.verify_checksums = verify;
126 self
127 }
128
129 pub fn lzma2_handle(&self) -> Lzma2Handle {
131 Lzma2Handle {
132 control: Arc::clone(&self.lzma2),
133 }
134 }
135
136 pub fn set_sub_stream_complete_hook(
139 &mut self,
140 hook: impl FnMut(SubStreamCompletion) + Send + 'static,
141 ) {
142 self.on_sub_stream_complete = Some(Box::new(hook));
143 }
144
145 pub fn entries(&self) -> &[ArchiveEntry] {
147 let start = self.archive.stream_map.block_first_file_index[self.block_index];
148 let count = self.archive.blocks[self.block_index].num_unpack_sub_streams;
149 &self.archive.files[start..start + count]
150 }
151
152 pub fn for_each_entries<F>(self, each: &mut F) -> Result<bool, Error>
159 where
160 F: FnMut(&ArchiveEntry, &mut dyn Read) -> Result<bool, Error>,
161 {
162 self.for_each_entries_direct(&mut |entry, reader| each(entry, reader))
163 }
164
165 pub fn for_each_entries_direct<F>(mut self, each: &mut F) -> Result<bool, Error>
170 where
171 F: FnMut(&ArchiveEntry, &mut dyn EntryRead) -> Result<bool, Error>,
172 {
173 let mut cursor = ReadAtCursor::new(Arc::clone(&self.source));
174 let decoder = BlockDecoder {
175 thread_count: self.thread_count,
176 adaptive_lzma2: self.adaptive_lzma2,
177 input_complete: false,
179 positional: None,
180 verify_checksums: self.verify_checksums,
181 lzma2: self.lzma2,
182 block_index: self.block_index,
183 archive: &self.archive,
184 password: &self.password,
185 source: &mut cursor,
186 limits: self.limits,
187 on_sub_stream_complete: self
188 .on_sub_stream_complete
189 .as_mut()
190 .map(|hook| &mut **hook as &mut (dyn FnMut(SubStreamCompletion) + Send)),
191 };
192 decoder.for_each_entries_direct(each)
193 }
194}