Skip to main content

conmonrs/
tail.rs

1//! A buffer which keeps only the end of what got written to it.
2
3/// Keeps the last `MAX` bytes of what got pushed, like the end of the stderr
4/// of a runtime, which holds its error.
5#[derive(Debug, Default)]
6pub(crate) struct Tail<const MAX: usize> {
7    buf: Vec<u8>,
8}
9
10impl<const MAX: usize> Tail<MAX> {
11    /// Append `data`, of which only the last `MAX` bytes are kept.
12    pub(crate) fn push(&mut self, data: &[u8]) {
13        // Only the end of a large push is kept, it does not get copied whole.
14        let data = &data[data.len().saturating_sub(MAX)..];
15        // Dropping the front only once it doubled keeps it amortized.
16        if self.buf.len() + data.len() > 2 * MAX {
17            self.buf.drain(..self.buf.len() - (MAX - data.len()));
18        }
19        self.buf.extend_from_slice(data);
20    }
21
22    /// The last `MAX` bytes.
23    pub(crate) fn end(&self) -> &[u8] {
24        &self.buf[self.buf.len().saturating_sub(MAX)..]
25    }
26
27    /// The last `MAX` bytes, without copying them.
28    pub(crate) fn into_end(mut self) -> Vec<u8> {
29        let excess = self.buf.len().saturating_sub(MAX);
30        self.buf.drain(..excess);
31        self.buf
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn keeps_the_end() {
41        let mut tail = Tail::<4>::default();
42        tail.push(b"ab");
43        assert_eq!(tail.end(), b"ab");
44        for i in 0..100u8 {
45            tail.push(&[i]);
46            assert!(tail.buf.len() <= 8);
47        }
48        assert_eq!(tail.end(), [96, 97, 98, 99]);
49
50        // A large push does not grow the buffer beyond its bound.
51        tail.push(&(0..100).collect::<Vec<u8>>());
52        assert!(tail.buf.capacity() <= 8);
53        assert_eq!(tail.end(), [96, 97, 98, 99]);
54        tail.push(&[1, 2]);
55        assert_eq!(tail.end(), [98, 99, 1, 2]);
56        assert_eq!(tail.into_end(), [98, 99, 1, 2]);
57    }
58}