Skip to main content

libflate_lz77/
default.rs

1use alloc::vec::Vec;
2use core::cmp;
3#[cfg(not(feature = "std"))]
4use hashbrown::HashMap;
5#[cfg(feature = "std")]
6use std::collections::HashMap;
7
8use super::Code;
9use super::Lz77Encode;
10use super::Sink;
11
12/// A [`Lz77Encode`] implementation used by default.
13#[derive(Debug)]
14pub struct DefaultLz77Encoder {
15    window_size: u16,
16    max_length: u16,
17    buf: Vec<u8>,
18}
19
20impl DefaultLz77Encoder {
21    /// Makes a new encoder instance.
22    ///
23    /// # Examples
24    /// ```
25    /// use libflate_lz77::{self as lz77, DefaultLz77Encoder, Lz77Encode};
26    ///
27    /// let lz77 = DefaultLz77Encoder::new();
28    /// assert_eq!(lz77.window_size(), lz77::MAX_WINDOW_SIZE);
29    /// ```
30    pub fn new() -> Self {
31        DefaultLz77EncoderBuilder::new().build()
32    }
33
34    /// Makes a new encoder instance with specified window size.
35    ///
36    /// Larger window size is prefered to raise compression ratio,
37    /// but it may require more working memory to encode and decode data.
38    ///
39    /// # Examples
40    /// ```
41    /// use libflate_lz77::{self as lz77, DefaultLz77Encoder, Lz77Encode};
42    ///
43    /// let lz77 = DefaultLz77Encoder::with_window_size(1024);
44    /// assert_eq!(lz77.window_size(), 1024);
45    /// ```
46    pub fn with_window_size(size: u16) -> Self {
47        DefaultLz77EncoderBuilder::new()
48            .window_size(cmp::min(size, super::MAX_WINDOW_SIZE))
49            .build()
50    }
51}
52
53impl Default for DefaultLz77Encoder {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl Lz77Encode for DefaultLz77Encoder {
60    fn encode<S>(&mut self, buf: &[u8], sink: S)
61    where
62        S: Sink,
63    {
64        self.buf.extend_from_slice(buf);
65        if self.buf.len() >= self.window_size as usize * 8 {
66            self.flush(sink);
67        }
68    }
69    fn flush<S>(&mut self, mut sink: S)
70    where
71        S: Sink,
72    {
73        let mut prefix_table = PrefixTable::new(self.buf.len());
74        let mut i = 0;
75        let end = cmp::max(3, self.buf.len()) - 3;
76        while i < end {
77            let key = prefix(&self.buf[i..]);
78            let matched = prefix_table.insert(key, i as u32);
79            if let Some(j) = matched.map(|j| j as usize) {
80                let distance = i - j;
81                if distance <= self.window_size as usize {
82                    let length = 3 + longest_common_prefix(
83                        &self.buf,
84                        i + 3,
85                        j + 3,
86                        self.max_length as usize,
87                    );
88                    sink.consume(Code::Pointer {
89                        length,
90                        backward_distance: distance as u16,
91                    });
92                    for k in (i..).take(length as usize).skip(1) {
93                        if k >= end {
94                            break;
95                        }
96                        prefix_table.insert(prefix(&self.buf[k..]), k as u32);
97                    }
98                    i += length as usize;
99                    continue;
100                }
101            }
102            sink.consume(Code::Literal(self.buf[i]));
103            i += 1;
104        }
105        for b in &self.buf[i..] {
106            sink.consume(Code::Literal(*b));
107        }
108        self.buf.clear();
109    }
110    fn window_size(&self) -> u16 {
111        self.window_size
112    }
113}
114
115#[inline]
116fn prefix(input_buf: &[u8]) -> [u8; 3] {
117    let buf: &[u8] = &input_buf[..3]; // perform bounds check once
118    [buf[0], buf[1], buf[2]]
119}
120
121#[inline]
122fn longest_common_prefix(buf: &[u8], i: usize, j: usize, max: usize) -> u16 {
123    buf[i..]
124        .iter()
125        .take(max - 3)
126        .zip(&buf[j..])
127        .take_while(|&(x, y)| x == y)
128        .count() as u16
129}
130
131#[derive(Debug)]
132enum PrefixTable {
133    Small(HashMap<[u8; 3], u32>),
134    Large(LargePrefixTable),
135}
136impl PrefixTable {
137    fn new(bytes: usize) -> Self {
138        if bytes < super::MAX_WINDOW_SIZE as usize {
139            PrefixTable::Small(HashMap::new())
140        } else {
141            PrefixTable::Large(LargePrefixTable::new())
142        }
143    }
144
145    #[inline]
146    fn insert(&mut self, prefix: [u8; 3], position: u32) -> Option<u32> {
147        match *self {
148            PrefixTable::Small(ref mut x) => x.insert(prefix, position),
149            PrefixTable::Large(ref mut x) => x.insert(prefix, position),
150        }
151    }
152}
153
154#[derive(Debug)]
155struct LargePrefixTable {
156    table: Vec<Vec<(u8, u32)>>,
157}
158impl LargePrefixTable {
159    fn new() -> Self {
160        LargePrefixTable {
161            table: (0..=0xFFFF).map(|_| Vec::new()).collect(),
162        }
163    }
164
165    #[inline]
166    fn insert(&mut self, prefix: [u8; 3], position: u32) -> Option<u32> {
167        let p0 = prefix[0] as usize;
168        let p1 = prefix[1] as usize;
169        let p2 = prefix[2];
170
171        let i = (p0 << 8) + p1;
172        let positions = &mut self.table[i];
173        for &mut (key, ref mut value) in positions.iter_mut() {
174            if key == p2 {
175                let old = *value;
176                *value = position;
177                return Some(old);
178            }
179        }
180        positions.push((p2, position));
181        None
182    }
183}
184
185/// Type for constructing instances of [`DefaultLz77Encoder`].
186///
187/// # Examples
188/// ```
189/// use libflate_lz77::{
190///     DefaultLz77EncoderBuilder,
191///     MAX_LENGTH,
192///     MAX_WINDOW_SIZE,
193/// };
194///
195/// // Produce an encoder explicitly with the default window size and max copy length
196/// let _encoder = DefaultLz77EncoderBuilder::new()
197///     .window_size(MAX_WINDOW_SIZE)
198///     .max_length(MAX_LENGTH)
199///     .build();
200/// ```
201#[derive(Debug)]
202pub struct DefaultLz77EncoderBuilder {
203    window_size: u16,
204    max_length: u16,
205}
206
207impl DefaultLz77EncoderBuilder {
208    /// Create a builder with the default parameters for the encoder.
209    pub fn new() -> Self {
210        DefaultLz77EncoderBuilder {
211            window_size: super::MAX_WINDOW_SIZE,
212            max_length: super::MAX_LENGTH,
213        }
214    }
215
216    /// Set the size of the sliding search window used during compression.
217    ///
218    /// Larger values require more memory. The standard window size may be
219    /// unsuitable for a particular Sink; for example, if the encoding used
220    /// cannot express pointer distances past a certain size, you would want the
221    /// window size to be no greater than the Sink's limit.
222    pub fn window_size(self, window_size: u16) -> Self {
223        DefaultLz77EncoderBuilder {
224            window_size: cmp::min(window_size, super::MAX_WINDOW_SIZE),
225            ..self
226        }
227    }
228
229    /// Set the maximum length of a pointer command this encoder will emit.
230    ///
231    /// Some uses of LZ77 may not be able to encode pointers of the standard
232    /// maximum length of 258 bytes. In this case, you may set your own maximum
233    /// which can be encoded by the Sink.
234    pub fn max_length(self, max_length: u16) -> Self {
235        DefaultLz77EncoderBuilder {
236            max_length: cmp::min(max_length, super::MAX_LENGTH),
237            ..self
238        }
239    }
240
241    /// Build the encoder with the builder state's parameters.
242    pub fn build(self) -> DefaultLz77Encoder {
243        DefaultLz77Encoder {
244            window_size: self.window_size,
245            max_length: self.max_length,
246            buf: Vec::new(),
247        }
248    }
249}
250
251impl Default for DefaultLz77EncoderBuilder {
252    fn default() -> Self {
253        Self::new()
254    }
255}