deflate/
lzvalue.rs

1#[cfg(test)]
2use crate::huffman_table::MAX_MATCH;
3use crate::huffman_table::{MAX_DISTANCE, MIN_MATCH};
4
5#[derive(Copy, Clone, Eq, PartialEq, Debug)]
6pub struct StoredLength {
7    length: u8,
8}
9
10impl StoredLength {
11    #[cfg(test)]
12    pub fn from_actual_length(length: u16) -> StoredLength {
13        assert!(length <= MAX_MATCH && length >= MIN_MATCH);
14        StoredLength {
15            length: (length - MIN_MATCH) as u8,
16        }
17    }
18
19    pub const fn new(stored_length: u8) -> StoredLength {
20        StoredLength {
21            length: stored_length,
22        }
23    }
24
25    pub const fn stored_length(&self) -> u8 {
26        self.length
27    }
28
29    #[cfg(test)]
30    pub fn actual_length(&self) -> u16 {
31        u16::from(self.length) + MIN_MATCH
32    }
33}
34
35#[derive(Copy, Clone, Eq, PartialEq, Debug)]
36pub enum LZType {
37    Literal(u8),
38    StoredLengthDistance(StoredLength, u16),
39}
40
41#[derive(Copy, Clone, Eq, PartialEq, Debug)]
42pub struct LZValue {
43    litlen: u8,
44    distance: u16,
45}
46
47impl LZValue {
48    #[inline]
49    pub const fn literal(value: u8) -> LZValue {
50        LZValue {
51            litlen: value,
52            distance: 0,
53        }
54    }
55
56    /// Create length-distance pair.
57    #[inline]
58    pub fn length_distance(length: u16, distance: u16) -> LZValue {
59        // TODO: Enforce min/max without too much perf penalty.
60        debug_assert!(distance > 0 && distance <= MAX_DISTANCE);
61        let stored_length = (length - MIN_MATCH) as u8;
62        LZValue {
63            litlen: stored_length,
64            distance,
65        }
66    }
67
68    #[inline]
69    pub fn value(&self) -> LZType {
70        if self.distance != 0 {
71            LZType::StoredLengthDistance(StoredLength::new(self.litlen), self.distance)
72        } else {
73            LZType::Literal(self.litlen)
74        }
75    }
76}
77
78#[cfg(test)]
79pub fn lit(l: u8) -> LZValue {
80    LZValue::literal(l)
81}
82
83#[cfg(test)]
84pub fn ld(l: u16, d: u16) -> LZValue {
85    LZValue::length_distance(l, d)
86}
87
88#[cfg(test)]
89mod test {
90    use super::*;
91    use crate::huffman_table::{MAX_DISTANCE, MAX_MATCH, MIN_DISTANCE, MIN_MATCH};
92    #[test]
93    fn lzvalue() {
94        for i in 0..255 as usize + 1 {
95            let v = LZValue::literal(i as u8);
96            if let LZType::Literal(n) = v.value() {
97                assert_eq!(n as usize, i);
98            } else {
99                panic!();
100            }
101        }
102
103        for i in MIN_MATCH..MAX_MATCH + 1 {
104            let v = LZValue::length_distance(i, 5);
105            if let LZType::StoredLengthDistance(l, _) = v.value() {
106                assert_eq!(l.actual_length(), i);
107            } else {
108                panic!();
109            }
110        }
111
112        for i in MIN_DISTANCE..MAX_DISTANCE + 1 {
113            let v = LZValue::length_distance(5, i);
114
115            if let LZType::StoredLengthDistance(_, d) = v.value() {
116                assert_eq!(d, i);
117            } else {
118                panic!("Failed to get distance {}", i);
119            }
120        }
121    }
122}