Skip to main content

lz4rip_core/
sink.rs

1use crate::fastcpy::slice_copy;
2
3/// Trait for output buffers used during compression and decompression.
4pub trait Sink {
5    /// Pushes a byte to the end of the Sink.
6    fn push(&mut self, byte: u8);
7
8    /// Current write position.
9    fn pos(&self) -> usize;
10
11    /// Total capacity of the underlying buffer.
12    fn capacity(&self) -> usize;
13
14    /// Extends the Sink with `data`.
15    fn extend_from_slice(&mut self, data: &[u8]);
16
17    /// Extends the Sink with `data`, but only advances `pos` by `copy_len`.
18    /// Allows overcopying into the trailing slack for wildcopy.
19    fn extend_from_slice_wild(&mut self, data: &[u8], copy_len: usize);
20
21    /// Copies `num_bytes` from `start` within the output buffer, handling
22    /// overlapping (periodic) patterns byte by byte.
23    fn extend_from_within_overlapping(&mut self, start: usize, num_bytes: usize);
24
25    /// Returns the underlying buffer and a mutable reference to the position.
26    fn output_mut_with_pos(&mut self) -> (&mut [u8], &mut usize);
27}
28
29/// `SliceSink` writes into a preallocated `&mut [u8]`.
30///
31/// # Handling of Capacity
32/// Extend methods will panic if there's insufficient capacity left in the Sink.
33///
34/// # Invariants
35///   - Bytes `[..pos()]` are always initialized.
36pub struct SliceSink<'a> {
37    output: &'a mut [u8],
38    pos: usize,
39}
40
41impl<'a> SliceSink<'a> {
42    /// Creates a `Sink` backed by the given byte slice.
43    /// `pos` defines the initial output position in the Sink.
44    /// # Panics
45    /// Panics if `pos` is out of bounds.
46    #[inline]
47    pub fn new(output: &'a mut [u8], pos: usize) -> Self {
48        let _ = &mut output[..pos]; // bounds check pos
49        SliceSink { output, pos }
50    }
51}
52
53impl Sink for SliceSink<'_> {
54    #[inline]
55    fn push(&mut self, byte: u8) {
56        self.output[self.pos] = byte;
57        self.pos += 1;
58    }
59
60    #[inline]
61    fn pos(&self) -> usize {
62        self.pos
63    }
64
65    #[inline]
66    fn capacity(&self) -> usize {
67        self.output.len()
68    }
69
70    #[inline]
71    fn extend_from_slice(&mut self, data: &[u8]) {
72        self.extend_from_slice_wild(data, data.len());
73    }
74
75    #[inline]
76    fn extend_from_slice_wild(&mut self, data: &[u8], copy_len: usize) {
77        assert!(copy_len <= data.len());
78        slice_copy(data, &mut self.output[self.pos..(self.pos) + data.len()]);
79        self.pos += copy_len;
80    }
81
82    #[inline]
83    #[cfg_attr(feature = "nightly", optimize(size))]
84    fn extend_from_within_overlapping(&mut self, start: usize, num_bytes: usize) {
85        let offset = self.pos - start;
86        if offset == 1 {
87            let val = self.output[start];
88            self.output[self.pos..self.pos + num_bytes].fill(val);
89        } else {
90            // Seed at most `num_bytes` bytes. When a dict-spanning match leaves
91            // an output remainder shorter than `offset`, copying a full
92            // `offset`-byte chunk would overshoot the output buffer even though
93            // the caller's `pos + num_bytes <= capacity` check held. The unsafe
94            // `copy_within_overlapping` clamps the same way (`offset.min(len)`).
95            let initial = offset.min(num_bytes);
96            self.output.copy_within(start..start + initial, self.pos);
97            let mut copied = initial;
98            while copied < num_bytes {
99                let n = copied.min(num_bytes - copied);
100                let src = self.pos;
101                self.output.copy_within(src..src + n, src + copied);
102                copied += n;
103            }
104        }
105        self.pos += num_bytes;
106    }
107
108    #[inline]
109    fn output_mut_with_pos(&mut self) -> (&mut [u8], &mut usize) {
110        (self.output, &mut self.pos)
111    }
112}
113
114#[cfg(test)]
115mod tests {
116
117    #[test]
118    fn test_sink_slice() {
119        use crate::sink::Sink;
120        use crate::sink::SliceSink;
121        let mut data = vec![0; 5];
122        let sink = SliceSink::new(&mut data, 1);
123        assert_eq!(sink.pos(), 1);
124        assert_eq!(sink.capacity(), 5);
125    }
126}