Skip to main content

haagenti_simd/
memops.rs

1//! Memory operations for compression.
2//!
3//! Optimized memory copy and fill operations used in LZ decompression.
4
5/// Copy a match from earlier in the output buffer.
6///
7/// This handles overlapping copies correctly, which is essential for LZ77
8/// decompression where the match source overlaps with the destination.
9///
10/// # Arguments
11///
12/// * `output` - Output buffer (destination is at end, source is earlier)
13/// * `offset` - Distance back from current position to match start
14/// * `length` - Number of bytes to copy
15///
16/// # Panics
17///
18/// Panics if offset is greater than output length or if there's not enough
19/// space for the copy.
20#[inline]
21pub fn copy_match(output: &mut Vec<u8>, offset: usize, length: usize) {
22    debug_assert!(offset > 0, "offset must be positive");
23    debug_assert!(offset <= output.len(), "offset exceeds buffer");
24
25    let start = output.len() - offset;
26
27    // Handle overlapping copies (common in LZ77)
28    if offset >= length {
29        // Non-overlapping: can use efficient extend
30        output.reserve(length);
31
32        // Safety: we've reserved space and verified bounds
33        unsafe {
34            let ptr = output.as_ptr().add(start);
35            let dst = output.as_mut_ptr().add(output.len());
36            std::ptr::copy_nonoverlapping(ptr, dst, length);
37            output.set_len(output.len() + length);
38        }
39    } else {
40        // Overlapping: copy byte by byte (repeat pattern)
41        output.reserve(length);
42
43        // For short offsets, unroll the pattern
44        if offset == 1 {
45            // Run-length encoding: repeat single byte
46            let byte = output[start];
47            output.extend(std::iter::repeat_n(byte, length));
48        } else if offset < 8 {
49            // Small offset: copy pattern repeatedly
50            for i in 0..length {
51                let byte = output[start + (i % offset)];
52                output.push(byte);
53            }
54        } else {
55            // Medium offset: copy in chunks
56            let mut remaining = length;
57            while remaining > 0 {
58                let copy_len = remaining.min(offset);
59                let src_start = output.len() - offset;
60                output.reserve(copy_len);
61
62                unsafe {
63                    let ptr = output.as_ptr().add(src_start);
64                    let dst = output.as_mut_ptr().add(output.len());
65                    std::ptr::copy_nonoverlapping(ptr, dst, copy_len);
66                    output.set_len(output.len() + copy_len);
67                }
68
69                remaining -= copy_len;
70            }
71        }
72    }
73}
74
75/// Fill buffer with a repeating pattern.
76///
77/// Used for run-length encoding where a pattern repeats many times.
78///
79/// # Arguments
80///
81/// * `output` - Output buffer to extend
82/// * `pattern` - Pattern to repeat
83/// * `count` - Number of times to repeat the pattern
84#[inline]
85pub fn fill_repeat(output: &mut Vec<u8>, pattern: &[u8], count: usize) {
86    if pattern.is_empty() || count == 0 {
87        return;
88    }
89
90    let total_len = pattern.len() * count;
91    output.reserve(total_len);
92
93    if pattern.len() == 1 {
94        // Single byte: use extend with repeat iterator
95        output.extend(std::iter::repeat_n(pattern[0], count));
96    } else {
97        // Multi-byte pattern: copy repeatedly
98        for _ in 0..count {
99            output.extend_from_slice(pattern);
100        }
101    }
102}
103
104/// Copy bytes from source to destination with potential overlap.
105///
106/// This is a building block for match copying that handles the case
107/// where source and destination may overlap.
108#[inline]
109pub fn copy_within_extend(output: &mut Vec<u8>, src_start: usize, length: usize) {
110    output.reserve(length);
111
112    let offset = output.len() - src_start;
113
114    if offset >= length {
115        // Non-overlapping
116        unsafe {
117            let ptr = output.as_ptr().add(src_start);
118            let dst = output.as_mut_ptr().add(output.len());
119            std::ptr::copy_nonoverlapping(ptr, dst, length);
120            output.set_len(output.len() + length);
121        }
122    } else {
123        // Overlapping - copy in chunks
124        let mut copied = 0;
125        while copied < length {
126            let chunk = (length - copied).min(offset);
127            unsafe {
128                let ptr = output.as_ptr().add(src_start + (copied % offset));
129                let dst = output.as_mut_ptr().add(output.len());
130                std::ptr::copy_nonoverlapping(ptr, dst, chunk);
131                output.set_len(output.len() + chunk);
132            }
133            copied += chunk;
134        }
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[test]
143    fn test_copy_match_non_overlapping() {
144        let mut output = vec![1, 2, 3, 4, 5];
145        copy_match(&mut output, 5, 3);
146        assert_eq!(output, vec![1, 2, 3, 4, 5, 1, 2, 3]);
147    }
148
149    #[test]
150    fn test_copy_match_overlapping() {
151        // Copy with small offset (pattern repeat)
152        let mut output = vec![b'A', b'B', b'C'];
153        copy_match(&mut output, 2, 6);
154        assert_eq!(output, b"ABCBCBCBC");
155    }
156
157    #[test]
158    fn test_copy_match_rle() {
159        // offset=1 is run-length encoding
160        let mut output = vec![b'X'];
161        copy_match(&mut output, 1, 5);
162        assert_eq!(output, b"XXXXXX");
163    }
164
165    #[test]
166    fn test_copy_match_offset_3() {
167        let mut output = vec![b'A', b'B', b'C'];
168        copy_match(&mut output, 3, 9);
169        assert_eq!(output, b"ABCABCABCABC");
170    }
171
172    #[test]
173    fn test_fill_repeat_single() {
174        let mut output = Vec::new();
175        fill_repeat(&mut output, b"X", 5);
176        assert_eq!(output, b"XXXXX");
177    }
178
179    #[test]
180    fn test_fill_repeat_pattern() {
181        let mut output = Vec::new();
182        fill_repeat(&mut output, b"AB", 4);
183        assert_eq!(output, b"ABABABAB");
184    }
185
186    #[test]
187    fn test_fill_repeat_empty() {
188        let mut output = Vec::new();
189        fill_repeat(&mut output, b"", 5);
190        assert!(output.is_empty());
191
192        fill_repeat(&mut output, b"X", 0);
193        assert!(output.is_empty());
194    }
195
196    #[test]
197    fn test_copy_match_medium_offset() {
198        let mut output: Vec<u8> = (0..20).collect();
199        let original_len = output.len();
200        copy_match(&mut output, 10, 25);
201
202        // First 10 should be a copy of bytes 10-19
203        assert_eq!(
204            &output[original_len..original_len + 10],
205            &(10..20).collect::<Vec<u8>>()
206        );
207        // Pattern continues
208        assert_eq!(output.len(), original_len + 25);
209    }
210
211    #[test]
212    fn test_copy_within_extend_non_overlapping() {
213        let mut output = vec![1, 2, 3, 4, 5];
214        copy_within_extend(&mut output, 0, 3);
215        assert_eq!(output, vec![1, 2, 3, 4, 5, 1, 2, 3]);
216    }
217
218    #[test]
219    fn test_copy_within_extend_overlapping() {
220        let mut output = vec![1, 2, 3];
221        copy_within_extend(&mut output, 1, 5);
222        // Copies from position 1 (value=2), with pattern length 2
223        assert_eq!(output, vec![1, 2, 3, 2, 3, 2, 3, 2]);
224    }
225}