bytes_ringbuffer/
lib.rs

1//! This crate provides a simple ringbuffer that implements the [Buf](../bytes/trait.Buf.html) and
2//! [BufMut](../bytes/trait.BufMut.html) traits from the [bytes](../bytes/index.html) crate.
3//!
4//! ```
5//! # extern crate bytes_ringbuffer;
6//! # use bytes_ringbuffer::*;
7//! # fn main() {
8//! let mut buf = RingBuffer::new(4);
9//! buf.put_u16(1234);
10//! buf.put_u16(5671);
11//! assert_eq!(buf.get_u16(), 1234);
12//! assert_eq!(buf.get_u16(), 5671);
13//! # }
14//! ```
15extern crate bytes;
16
17use std::mem::MaybeUninit;
18
19pub use bytes::{Buf, BufMut};
20
21/// Fixed-capacity buffer
22#[derive(Debug, Clone)]
23pub struct RingBuffer {
24    buffer: Vec<MaybeUninit<u8>>,
25    begin: usize,
26    len: usize,
27}
28
29impl RingBuffer {
30    /// Create a ringbuffer with the given capacity
31    pub fn new(capacity: usize) -> Self {
32        Self {
33            buffer: vec![MaybeUninit::uninit(); capacity],
34            begin: 0,
35            len: 0,
36        }
37    }
38
39    /// Capacity of the ringbuffer
40    pub fn capacity(&self) -> usize {
41        self.buffer.len()
42    }
43}
44
45impl Buf for RingBuffer {
46    fn remaining(&self) -> usize {
47        self.len
48    }
49
50    fn bytes(&self) -> &[u8] {
51        let end = (self.begin + self.len).min(self.capacity());
52        let slice = &self.buffer[self.begin..end];
53        // Safe because `slice` is a subset of the bytes that have been declared
54        // initialized by the unsafe `BufMut::advance_mut` function.
55        unsafe { &*(slice as *const [MaybeUninit<u8>] as *const [u8]) }
56    }
57
58    fn advance(&mut self, cnt: usize) {
59        assert!(cnt <= self.len);
60        self.begin += cnt;
61        self.begin %= self.capacity();
62        self.len -= cnt;
63    }
64}
65
66impl BufMut for RingBuffer {
67    fn remaining_mut(&self) -> usize {
68        self.capacity() - self.remaining()
69    }
70
71    fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
72        let begin = (self.begin + self.len) % self.capacity();
73        let end = (begin + self.remaining_mut()).min(self.capacity());
74        &mut self.buffer[begin..end]
75    }
76
77    unsafe fn advance_mut(&mut self, cnt: usize) {
78        assert!(cnt <= self.remaining_mut());
79        self.len += cnt;
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn ringbuffer_init() {
89        let buf = RingBuffer::new(16);
90        assert_eq!(buf.capacity(), 16);
91        assert_eq!(buf.remaining(), 0);
92        assert_eq!(buf.remaining_mut(), 16);
93    }
94
95    #[test]
96    fn ringbuffer_read_write() {
97        let mut buf = RingBuffer::new(16);
98
99        // Write 1..6 to buffer
100        for i in 0..6 {
101            buf.put_u8(i);
102        }
103        assert_eq!(buf.remaining(), 6);
104        assert_eq!(buf.remaining_mut(), 10);
105        // Read 1..6 from buffer
106        for i in 0..6 {
107            assert_eq!(buf.get_u8(), i);
108        }
109
110        // Write over the end
111        for i in 0..16 {
112            buf.put_u8(i);
113        }
114        assert_eq!(buf.remaining(), 16);
115        assert_eq!(buf.remaining_mut(), 0);
116        // bytes() should be a slice of length 10
117        assert_eq!(buf.bytes().len(), 10);
118        for i in 0..10 {
119            assert_eq!(buf.get_u8(), i);
120        }
121        // Now bytes() should be a slice of length 6
122        assert_eq!(buf.bytes().len(), 6);
123        // Empty the buffer
124        for i in 10..16 {
125            assert_eq!(buf.get_u8(), i);
126        }
127    }
128
129    #[test]
130    #[should_panic]
131    fn ringbuffer_read_over_remaining() {
132        let mut buf = RingBuffer::new(16);
133        for i in 0..15 {
134            buf.put_u8(i);
135        }
136        for _ in 0..16 {
137            buf.get_u8();
138        }
139    }
140
141    #[test]
142    #[should_panic]
143    fn ringbuffer_write_over_capacity() {
144        let mut buf = RingBuffer::new(16);
145        for i in 0..17 {
146            buf.put_u8(i);
147        }
148    }
149}