Skip to main content

lzf_rust/
stream.rs

1// SPDX-License-Identifier: ISC
2use alloc::vec;
3use alloc::vec::Vec;
4
5use crate::decompress;
6#[cfg(feature = "encoder")]
7use crate::{AutoFinish, AutoFinisher, Error, Result, Write};
8#[cfg(feature = "encoder")]
9use crate::{CompressionMode, compress_with_mode};
10use crate::{Read, Result as DecodeResult};
11
12const MAGIC_0: u8 = b'Z';
13const MAGIC_1: u8 = b'V';
14const TYPE_UNCOMPRESSED: u8 = 0;
15const TYPE_COMPRESSED: u8 = 1;
16
17/// Reader that decodes framed LZF (`ZV` block stream).
18///
19/// The reader consumes blocks lazily and yields decompressed bytes through the
20/// crate's `Read` trait.
21///
22/// # Example
23///
24/// ```
25/// use lzf_rust::{LzfRead, LzfReader, encode_blocks};
26///
27/// let input = b"stream reader example";
28/// let framed = encode_blocks(input, 4096).unwrap();
29/// let mut src: &[u8] = &framed;
30/// let mut reader = LzfReader::new(&mut src);
31///
32/// let mut out = vec![0u8; input.len()];
33/// reader.read_exact(&mut out).unwrap();
34/// assert_eq!(out, input);
35/// ```
36pub struct LzfReader<R: Read> {
37    inner: R,
38    in_buf: Vec<u8>,
39    out_buf: Vec<u8>,
40    out_pos: usize,
41    finished: bool,
42}
43
44impl<R: Read> LzfReader<R> {
45    /// Creates a new framed LZF reader.
46    pub fn new(inner: R) -> Self {
47        Self { inner, in_buf: Vec::new(), out_buf: Vec::new(), out_pos: 0, finished: false }
48    }
49
50    /// Unwraps the reader and returns the underlying reader.
51    pub fn into_inner(self) -> R {
52        self.inner
53    }
54
55    /// Returns a shared reference to the underlying reader.
56    pub fn inner(&self) -> &R {
57        &self.inner
58    }
59
60    /// Returns a mutable reference to the underlying reader.
61    pub fn inner_mut(&mut self) -> &mut R {
62        &mut self.inner
63    }
64
65    fn load_next_block(&mut self) -> DecodeResult<bool> {
66        if self.finished {
67            return Ok(false);
68        }
69
70        let mut first = [0u8; 1];
71        let n = self.inner.read(&mut first)?;
72        if n == 0 || first[0] == 0 {
73            self.finished = true;
74            return Ok(false);
75        }
76
77        let mut rest = [0u8; 4];
78        self.inner.read_exact(&mut rest)?;
79
80        if first[0] != MAGIC_0 || rest[0] != MAGIC_1 {
81            return Err(crate::Error::InvalidHeader);
82        }
83
84        let block_type = rest[1];
85        match block_type {
86            TYPE_UNCOMPRESSED => {
87                let us = usize::from(u16::from_be_bytes([rest[2], rest[3]]));
88                self.out_buf.resize(us, 0);
89                self.inner.read_exact(&mut self.out_buf)?;
90                self.out_pos = 0;
91                Ok(true)
92            }
93            TYPE_COMPRESSED => {
94                let cs = usize::from(u16::from_be_bytes([rest[2], rest[3]]));
95                let mut us_buf = [0u8; 2];
96                self.inner.read_exact(&mut us_buf)?;
97                let us = usize::from(u16::from_be_bytes(us_buf));
98
99                self.in_buf.resize(cs, 0);
100                self.inner.read_exact(&mut self.in_buf)?;
101
102                self.out_buf.resize(us, 0);
103                let written = decompress(&self.in_buf, &mut self.out_buf)?;
104                if written != us {
105                    return Err(crate::Error::InvalidData);
106                }
107                self.out_pos = 0;
108                Ok(true)
109            }
110            other => Err(crate::Error::UnknownBlockType(other)),
111        }
112    }
113}
114
115impl<R: Read> Read for LzfReader<R> {
116    fn read(&mut self, buf: &mut [u8]) -> DecodeResult<usize> {
117        if buf.is_empty() {
118            return Ok(0);
119        }
120
121        let mut written = 0usize;
122        while written < buf.len() {
123            if self.out_pos < self.out_buf.len() {
124                let avail = self.out_buf.len() - self.out_pos;
125                let take = (buf.len() - written).min(avail);
126                buf[written..written + take]
127                    .copy_from_slice(&self.out_buf[self.out_pos..self.out_pos + take]);
128                self.out_pos += take;
129                written += take;
130                continue;
131            }
132
133            self.out_buf.clear();
134            self.out_pos = 0;
135            if !self.load_next_block()? {
136                break;
137            }
138        }
139
140        Ok(written)
141    }
142}
143
144/// Writer that encodes framed LZF (`ZV` block stream).
145///
146/// Data written into this adapter is chunked into blocks and emitted as either
147/// compressed or uncompressed `ZV` blocks.
148#[cfg(feature = "encoder")]
149pub struct LzfWriter<W: Write> {
150    inner: W,
151    block_size: usize,
152    mode: CompressionMode,
153    in_buf: Vec<u8>,
154    comp_buf: Vec<u8>,
155    write_eof_marker: bool,
156}
157
158#[cfg(feature = "encoder")]
159impl<W: Write> LzfWriter<W> {
160    /// Creates a new framed LZF writer with the given block size (`1..=65535`).
161    pub fn new(inner: W, block_size: usize) -> Result<Self> {
162        Self::new_with_mode(inner, block_size, CompressionMode::Normal)
163    }
164
165    /// Creates a new framed LZF writer with an explicit compression mode.
166    pub fn new_with_mode(inner: W, block_size: usize, mode: CompressionMode) -> Result<Self> {
167        if block_size == 0 || block_size > usize::from(u16::MAX) {
168            return Err(Error::InvalidParameter);
169        }
170        Ok(Self {
171            inner,
172            block_size,
173            mode,
174            in_buf: Vec::with_capacity(block_size),
175            comp_buf: vec![0u8; block_size.saturating_sub(4)],
176            write_eof_marker: false,
177        })
178    }
179
180    /// Creates a writer and enables writing a trailing zero byte EOF marker on finish.
181    ///
182    /// The marker matches the historical `lzf` utility stream behavior.
183    pub fn new_with_eof_marker(inner: W, block_size: usize) -> Result<Self> {
184        Self::new_with_eof_marker_and_mode(inner, block_size, CompressionMode::Normal)
185    }
186
187    /// Creates a writer and enables writing a trailing zero byte EOF marker on finish.
188    ///
189    /// Compression mode is explicitly selected.
190    pub fn new_with_eof_marker_and_mode(
191        inner: W,
192        block_size: usize,
193        mode: CompressionMode,
194    ) -> Result<Self> {
195        let mut this = Self::new_with_mode(inner, block_size, mode)?;
196        this.write_eof_marker = true;
197        Ok(this)
198    }
199
200    /// Unwraps the writer and returns the underlying writer.
201    pub fn into_inner(self) -> W {
202        self.inner
203    }
204
205    /// Returns a shared reference to the underlying writer.
206    pub fn inner(&self) -> &W {
207        &self.inner
208    }
209
210    /// Returns a mutable reference to the underlying writer.
211    pub fn inner_mut(&mut self) -> &mut W {
212        &mut self.inner
213    }
214
215    /// Finishes the stream and returns the underlying writer.
216    ///
217    /// This flushes any pending input block. If EOF marker mode is enabled, a
218    /// trailing zero byte is appended after the final block.
219    pub fn finish(mut self) -> Result<W> {
220        self.flush_pending()?;
221        if self.write_eof_marker {
222            self.inner.write_all(&[0])?;
223        }
224        self.inner.flush()?;
225        Ok(self.inner)
226    }
227
228    /// Returns a wrapper that will call `finish()` on drop.
229    ///
230    /// This is useful for best-effort stream finalization in scopes with early
231    /// returns.
232    pub fn auto_finish(self) -> AutoFinisher<Self> {
233        AutoFinisher(Some(self))
234    }
235
236    fn flush_pending(&mut self) -> Result<()> {
237        if !self.in_buf.is_empty() {
238            Self::write_block_into(&mut self.inner, self.mode, &mut self.comp_buf, &self.in_buf)?;
239            self.in_buf.clear();
240        }
241        Ok(())
242    }
243
244    fn write_block_into(
245        inner: &mut W,
246        mode: CompressionMode,
247        comp_buf: &mut Vec<u8>,
248        block: &[u8],
249    ) -> Result<()> {
250        let max_try = block.len().saturating_sub(4);
251        if max_try > 0 {
252            if comp_buf.len() < max_try {
253                comp_buf.resize(max_try, 0);
254            }
255            match compress_with_mode(block, &mut comp_buf[..max_try], mode) {
256                Ok(cs) => {
257                    let cs_u16 =
258                        u16::try_from(cs).map_err(|_| Error::InvalidParameter)?.to_be_bytes();
259                    let us_u16 = u16::try_from(block.len())
260                        .map_err(|_| Error::InvalidParameter)?
261                        .to_be_bytes();
262                    inner.write_all(&[MAGIC_0, MAGIC_1, TYPE_COMPRESSED])?;
263                    inner.write_all(&cs_u16)?;
264                    inner.write_all(&us_u16)?;
265                    inner.write_all(&comp_buf[..cs])?;
266                    return Ok(());
267                }
268                Err(Error::OutputTooSmall) => {}
269                Err(err) => return Err(err),
270            }
271        }
272
273        let us_u16 = u16::try_from(block.len()).map_err(|_| Error::InvalidParameter)?.to_be_bytes();
274        inner.write_all(&[MAGIC_0, MAGIC_1, TYPE_UNCOMPRESSED])?;
275        inner.write_all(&us_u16)?;
276        inner.write_all(block)?;
277        Ok(())
278    }
279}
280
281#[cfg(feature = "encoder")]
282impl<W: Write> AutoFinish for LzfWriter<W> {
283    fn finish_ignore_error(self) {
284        let _ = self.finish();
285    }
286}
287
288#[cfg(feature = "encoder")]
289impl<W: Write> Write for LzfWriter<W> {
290    fn write(&mut self, buf: &[u8]) -> Result<usize> {
291        let mut input = buf;
292
293        if !self.in_buf.is_empty() {
294            let need = self.block_size - self.in_buf.len();
295            let take = need.min(input.len());
296            self.in_buf.extend_from_slice(&input[..take]);
297            input = &input[take..];
298
299            if self.in_buf.len() == self.block_size {
300                Self::write_block_into(
301                    &mut self.inner,
302                    self.mode,
303                    &mut self.comp_buf,
304                    &self.in_buf,
305                )?;
306                self.in_buf.clear();
307            }
308        }
309
310        let mut consumed = 0usize;
311        while input.len() - consumed >= self.block_size {
312            let block = &input[consumed..consumed + self.block_size];
313            Self::write_block_into(&mut self.inner, self.mode, &mut self.comp_buf, block)?;
314            consumed += self.block_size;
315        }
316
317        if consumed < input.len() {
318            self.in_buf.extend_from_slice(&input[consumed..]);
319        }
320
321        Ok(buf.len())
322    }
323
324    fn flush(&mut self) -> Result<()> {
325        self.flush_pending()?;
326        self.inner.flush()
327    }
328}