lzma_rust2/lzip/
writer.rs1use core::num::NonZeroU64;
2
3use super::{
4 encode_dict_size, CRC32, HEADER_SIZE, LZIP_MAGIC, LZIP_VERSION, MAX_DICT_SIZE, MIN_DICT_SIZE,
5 TRAILER_SIZE,
6};
7use crate::{
8 enc::{LzmaOptions, LzmaWriter},
9 error_invalid_data, AutoFinish, AutoFinisher, ByteWriter, CountingWriter, Result, Write,
10};
11
12#[derive(Default, Debug, Clone)]
14pub struct LzipOptions {
15 pub lzma_options: LzmaOptions,
17 pub member_size: Option<NonZeroU64>,
20}
21
22impl LzipOptions {
23 pub fn with_preset(preset: u32) -> Self {
25 Self {
26 lzma_options: LzmaOptions::with_preset(preset),
27 member_size: None,
28 }
29 }
30
31 pub fn set_member_size(&mut self, member_size: Option<NonZeroU64>) {
33 self.member_size = member_size;
34 }
35}
36
37pub struct LzipWriter<W: Write> {
39 inner: Option<W>,
40 lzma_writer: Option<LzmaWriter<CountingWriter<W>>>,
41 options: LzipOptions,
42 header_written: bool,
43 finished: bool,
44 crc_digest: crc::Digest<'static, u32, crc::Table<16>>,
45 uncompressed_size: u64,
46 member_start_pos: u64,
47 current_member_uncompressed_size: u64,
48}
49
50impl<W: Write> LzipWriter<W> {
51 pub fn new(inner: W, options: LzipOptions) -> Self {
53 let mut options = options;
54
55 options.lzma_options.lc = 3;
57 options.lzma_options.lp = 0;
58 options.lzma_options.pb = 2;
59 options.lzma_options.dict_size = options
60 .lzma_options
61 .dict_size
62 .clamp(MIN_DICT_SIZE, MAX_DICT_SIZE);
63
64 if let Some(member_size) = options.member_size.as_mut() {
65 *member_size =
66 NonZeroU64::new(member_size.get().max(options.lzma_options.dict_size as u64))
67 .expect("member size is zero");
68 }
69
70 Self {
71 inner: Some(inner),
72 lzma_writer: None,
73 options,
74 header_written: false,
75 finished: false,
76 crc_digest: CRC32.digest(),
77 uncompressed_size: 0,
78 member_start_pos: 0,
79 current_member_uncompressed_size: 0,
80 }
81 }
82
83 pub fn auto_finish(self) -> AutoFinisher<Self> {
85 AutoFinisher(Some(self))
86 }
87
88 pub fn into_inner(mut self) -> W {
90 if let Some(lzma_writer) = self.lzma_writer.take() {
91 return lzma_writer.into_inner().into_inner();
92 }
93
94 self.inner.take().expect("inner writer not set")
95 }
96
97 pub fn inner(&self) -> &W {
99 self.lzma_writer
100 .as_ref()
101 .map(|reader| reader.inner().inner())
102 .unwrap_or_else(|| self.inner.as_ref().expect("inner writer not set"))
103 }
104
105 pub fn inner_mut(&mut self) -> &mut W {
107 self.lzma_writer
108 .as_mut()
109 .map(|reader| reader.inner_mut().inner_mut())
110 .unwrap_or_else(|| self.inner.as_mut().expect("inner writer not set"))
111 }
112
113 fn should_finish_member(&self) -> bool {
115 if let Some(member_size) = self.options.member_size {
116 self.current_member_uncompressed_size >= member_size.get()
117 } else {
118 false
119 }
120 }
121
122 fn start_new_member(&mut self) -> Result<()> {
124 let mut writer = self.inner.take().expect("inner writer not set");
125
126 self.member_start_pos = 0;
127
128 writer.write_all(&LZIP_MAGIC)?;
129 writer.write_all(&[LZIP_VERSION])?;
130
131 let dict_size_byte = encode_dict_size(self.options.lzma_options.dict_size)?;
132 writer.write_u8(dict_size_byte)?;
133
134 let counting_writer = CountingWriter::new(writer);
135
136 let lzma_writer =
137 LzmaWriter::new_no_header(counting_writer, &self.options.lzma_options, true)?;
138
139 self.lzma_writer = Some(lzma_writer);
140 self.header_written = true;
141 self.current_member_uncompressed_size = 0;
142 self.crc_digest = CRC32.digest();
143 self.uncompressed_size = 0;
144
145 Ok(())
146 }
147
148 fn write_header(&mut self) -> Result<()> {
149 if self.header_written {
150 return Ok(());
151 }
152
153 self.start_new_member()
154 }
155
156 fn finish_current_member(&mut self) -> Result<()> {
158 let lzma_writer = self.lzma_writer.take().expect("lzma writer not set");
159
160 let counting_writer = lzma_writer.finish()?;
161 let compressed_size = counting_writer.bytes_written();
162 let mut writer = counting_writer.into_inner();
163
164 let member_size = HEADER_SIZE as u64 + compressed_size + TRAILER_SIZE as u64;
166
167 let crc_digest = core::mem::replace(&mut self.crc_digest, CRC32.digest());
168 let computed_crc = crc_digest.finalize();
169 writer.write_u32(computed_crc)?;
170 writer.write_u64(self.uncompressed_size)?;
171 writer.write_u64(member_size)?;
172
173 self.inner = Some(writer);
174 self.header_written = false;
175
176 Ok(())
177 }
178
179 pub fn finish(mut self) -> Result<W> {
181 if self.finished {
182 return Ok(self.into_inner());
183 }
184
185 if !self.header_written {
186 self.write_header()?;
187 }
188
189 self.finish_current_member()?;
190 self.finished = true;
191
192 Ok(self.into_inner())
193 }
194}
195
196impl<W: Write> Write for LzipWriter<W> {
197 fn write(&mut self, buf: &[u8]) -> Result<usize> {
198 if self.finished {
199 return Err(error_invalid_data("LZIP writer already finished"));
200 }
201
202 if buf.is_empty() {
203 return Ok(0);
204 }
205
206 let mut total_written = 0;
207 let mut remaining = buf;
208
209 while !remaining.is_empty() {
210 if self.should_finish_member() && self.header_written {
211 self.finish_current_member()?;
212 }
213
214 if !self.header_written {
215 self.start_new_member()?;
216 }
217
218 let lzma_writer = self.lzma_writer.as_mut().expect("lzma writer not set");
219
220 let bytes_to_write = if let Some(member_size) = self.options.member_size {
221 let remaining_in_member = member_size
222 .get()
223 .saturating_sub(self.current_member_uncompressed_size);
224 (remaining.len() as u64).min(remaining_in_member) as usize
225 } else {
226 remaining.len()
227 };
228
229 if bytes_to_write == 0 {
230 self.finish_current_member()?;
231 continue;
232 }
233
234 let bytes_written = lzma_writer.write(&remaining[..bytes_to_write])?;
235
236 if bytes_written > 0 {
237 self.crc_digest.update(&remaining[..bytes_written]);
238 self.uncompressed_size += bytes_written as u64;
239 self.current_member_uncompressed_size += bytes_written as u64;
240 total_written += bytes_written;
241 remaining = &remaining[bytes_written..];
242 } else {
243 break;
244 }
245 }
246
247 Ok(total_written)
248 }
249
250 fn flush(&mut self) -> Result<()> {
251 if let Some(ref mut lzma_writer) = self.lzma_writer {
252 lzma_writer.flush()?;
253 }
254 Ok(())
255 }
256}
257
258impl<W: Write> AutoFinish for LzipWriter<W> {
259 fn finish_ignore_error(self) {
260 let _ = self.finish();
261 }
262}