Skip to main content

xchecker_runner/
ring_buffer.rs

1//! Ring buffer implementation for bounded output capture
2
3use std::collections::VecDeque;
4use std::fmt;
5
6/// A ring buffer that maintains a fixed maximum size
7#[derive(Debug, Clone)]
8pub struct RingBuffer {
9    buffer: VecDeque<u8>,
10    max_bytes: usize,
11    total_bytes_written: usize,
12}
13
14impl RingBuffer {
15    /// Create a new ring buffer with specified maximum size
16    #[must_use]
17    pub fn new(max_bytes: usize) -> Self {
18        Self {
19            buffer: VecDeque::with_capacity(max_bytes.min(8192)),
20            max_bytes,
21            total_bytes_written: 0,
22        }
23    }
24
25    /// Write data to ring buffer
26    ///
27    /// If the buffer would exceed `max_bytes`, old data is dropped from the front.
28    pub fn write(&mut self, data: &[u8]) {
29        self.total_bytes_written += data.len();
30
31        for &byte in data {
32            if self.buffer.len() >= self.max_bytes {
33                // Buffer is full, remove oldest byte
34                self.buffer.pop_front();
35            }
36            self.buffer.push_back(byte);
37        }
38    }
39
40    /// Get the current size of the buffer in bytes
41    #[must_use]
42    #[allow(dead_code)] // Standard collection API method
43    pub fn len(&self) -> usize {
44        self.buffer.len()
45    }
46
47    /// Check if buffer is empty
48    #[must_use]
49    #[allow(dead_code)] // Standard collection API method
50    pub fn is_empty(&self) -> bool {
51        self.buffer.is_empty()
52    }
53
54    /// Get the total number of bytes written (including truncated bytes)
55    #[must_use]
56    pub const fn total_bytes_written(&self) -> usize {
57        self.total_bytes_written
58    }
59
60    /// Check if any data was truncated
61    #[must_use]
62    pub const fn was_truncated(&self) -> bool {
63        self.total_bytes_written > self.max_bytes
64    }
65}
66
67impl fmt::Display for RingBuffer {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        let bytes: Vec<u8> = self.buffer.iter().copied().collect();
70        write!(f, "{}", String::from_utf8_lossy(&bytes))
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_ring_buffer_basic() {
80        let mut buffer = RingBuffer::new(10);
81        buffer.write(b"hello");
82        assert_eq!(buffer.to_string(), "hello");
83        assert_eq!(buffer.len(), 5);
84        assert!(!buffer.is_empty());
85    }
86
87    #[test]
88    fn test_ring_buffer_truncation() {
89        let mut buffer = RingBuffer::new(10);
90        buffer.write(b"hello");
91        buffer.write(b"world");
92        buffer.write(b"!");
93
94        // Total written: 11 bytes, but buffer only holds 10
95        assert_eq!(buffer.len(), 10);
96        assert_eq!(buffer.to_string(), "elloworld!");
97        assert_eq!(buffer.total_bytes_written(), 11);
98        assert!(buffer.was_truncated());
99    }
100
101    #[test]
102    fn test_ring_buffer_large_write() {
103        let mut buffer = RingBuffer::new(5);
104        buffer.write(b"hello world");
105
106        // Should only keep last 5 bytes
107        assert_eq!(buffer.len(), 5);
108        assert_eq!(buffer.to_string(), "world");
109        assert_eq!(buffer.total_bytes_written(), 11);
110        assert!(buffer.was_truncated());
111    }
112
113    #[test]
114    fn test_ring_buffer_exact_capacity() {
115        let mut buffer = RingBuffer::new(10);
116        buffer.write(b"1234567890");
117
118        assert_eq!(buffer.len(), 10);
119        assert_eq!(buffer.to_string(), "1234567890");
120        assert!(!buffer.was_truncated());
121    }
122
123    #[test]
124    fn test_ring_buffer_multiple_writes() {
125        let mut buffer = RingBuffer::new(10);
126        buffer.write(b"12345");
127        buffer.write(b"67890");
128        buffer.write(b"ABCDE");
129
130        // Should keep last 10 bytes: "67890ABCDE"
131        assert_eq!(buffer.len(), 10);
132        assert_eq!(buffer.to_string(), "67890ABCDE");
133        assert_eq!(buffer.total_bytes_written(), 15);
134        assert!(buffer.was_truncated());
135    }
136
137    #[test]
138    fn test_ring_buffer_empty() {
139        let buffer = RingBuffer::new(10);
140        assert!(buffer.is_empty());
141        assert_eq!(buffer.len(), 0);
142        assert_eq!(buffer.to_string(), "");
143        assert!(!buffer.was_truncated());
144    }
145
146    #[test]
147    fn test_ring_buffer_utf8_handling() {
148        let mut buffer = RingBuffer::new(20);
149        buffer.write("Hello 世界".as_bytes());
150        assert_eq!(buffer.to_string(), "Hello 世界");
151    }
152
153    #[test]
154    fn test_ring_buffer_invalid_utf8() {
155        let mut buffer = RingBuffer::new(10);
156        // Write invalid UTF-8 sequence
157        buffer.write(&[0xFF, 0xFE, 0xFD]);
158        // Should use replacement character
159        let result = buffer.to_string();
160        assert!(!result.is_empty());
161    }
162}