lzma_rust2/enc/
lzma_writer.rs1use super::{
2 encoder::{LzmaEncoder, LzmaEncoderModes},
3 range_enc::RangeEncoder,
4 LzmaOptions,
5};
6use crate::{error_invalid_input, error_unsupported, AutoFinish, AutoFinisher, Write};
7
8pub struct LzmaWriter<W: Write> {
10 rc: RangeEncoder<W>,
11 lzma: LzmaEncoder,
12 use_end_marker: bool,
13 current_uncompressed_size: u64,
14 expected_uncompressed_size: Option<u64>,
15 props: u8,
16 mode: LzmaEncoderModes,
17}
18
19impl<W: Write> LzmaWriter<W> {
20 pub fn new(
22 mut out: W,
23 options: &LzmaOptions,
24 use_header: bool,
25 use_end_marker: bool,
26 expected_uncompressed_size: Option<u64>,
27 ) -> crate::Result<LzmaWriter<W>> {
28 let (mut lzma, mode) = LzmaEncoder::new(
29 options.mode,
30 options.lc,
31 options.lp,
32 options.pb,
33 options.mf,
34 options.depth_limit,
35 options.dict_size,
36 options.nice_len as usize,
37 );
38 if let Some(preset_dict) = &options.preset_dict {
39 if use_header {
40 return Err(error_unsupported(
41 "header is not supported with preset dict",
42 ));
43 }
44 lzma.lz.set_preset_dict(options.dict_size, preset_dict);
45 }
46
47 let props = options.get_props();
48 if use_header {
49 out.write_all(&[props])?;
50 let dict_size = options.dict_size;
51 out.write_all(&dict_size.to_le_bytes())?;
52 let expected_compressed_size = expected_uncompressed_size.unwrap_or(u64::MAX);
53 out.write_all(&expected_compressed_size.to_le_bytes())?;
54 }
55
56 let rc = RangeEncoder::new(out);
57 Ok(LzmaWriter {
58 rc,
59 lzma,
60 use_end_marker,
61 current_uncompressed_size: 0,
62 expected_uncompressed_size,
63 props,
64 mode,
65 })
66 }
67
68 #[inline]
70 pub fn new_use_header(
71 out: W,
72 options: &LzmaOptions,
73 input_size: Option<u64>,
74 ) -> crate::Result<Self> {
75 Self::new(out, options, true, input_size.is_none(), input_size)
76 }
77
78 #[inline]
80 pub fn new_no_header(
81 out: W,
82 options: &LzmaOptions,
83 use_end_marker: bool,
84 ) -> crate::Result<Self> {
85 Self::new(out, options, false, use_end_marker, None)
86 }
87
88 pub fn auto_finish(self) -> AutoFinisher<Self> {
90 AutoFinisher(Some(self))
91 }
92
93 #[inline]
95 pub fn props(&self) -> u8 {
96 self.props
97 }
98
99 #[inline]
101 pub fn get_uncompressed_size(&self) -> u64 {
102 self.current_uncompressed_size
103 }
104
105 pub fn into_inner(self) -> W {
107 self.rc.into_inner()
108 }
109
110 pub fn inner(&self) -> &W {
112 self.rc.inner()
113 }
114
115 pub fn inner_mut(&mut self) -> &mut W {
117 self.rc.inner_mut()
118 }
119
120 pub fn finish(mut self) -> crate::Result<W> {
122 if let Some(exp) = self.expected_uncompressed_size {
123 if exp != self.current_uncompressed_size {
124 return Err(error_invalid_input(
125 "expected compressed size does not match actual compressed size",
126 ));
127 }
128 }
129 self.lzma.lz.set_finishing();
130 self.lzma.encode_for_lzma1(&mut self.rc, &mut self.mode)?;
131 if self.use_end_marker {
132 self.lzma.encode_lzma1_end_marker(&mut self.rc)?;
133 }
134 self.rc.finish()?;
135
136 let Self { rc, .. } = self;
137
138 Ok(rc.into_inner())
139 }
140}
141
142impl<W: Write> Write for LzmaWriter<W> {
143 fn write(&mut self, buf: &[u8]) -> crate::Result<usize> {
144 if let Some(exp) = self.expected_uncompressed_size {
145 if exp < self.current_uncompressed_size + buf.len() as u64 {
146 return Err(error_invalid_input(
147 "expected compressed size does not match actual compressed size",
148 ));
149 }
150 }
151 self.current_uncompressed_size += buf.len() as u64;
152 let mut len = buf.len();
153 let mut off = 0;
154 while len > 0 {
155 let used = self.lzma.lz.fill_window(&buf[off..]);
156 off += used;
157 len -= used;
158 self.lzma.encode_for_lzma1(&mut self.rc, &mut self.mode)?;
159 }
160
161 Ok(off)
162 }
163
164 fn flush(&mut self) -> crate::Result<()> {
165 Ok(())
166 }
167}
168
169impl<W: Write> AutoFinish for LzmaWriter<W> {
170 fn finish_ignore_error(self) {
171 let _ = self.finish();
172 }
173}