Skip to content

Commit b6c264c

Browse files
committed
feat(utils): add RingBuffer struct
Add `RingBuffer` data structure. It will be used in later commits. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 53381f7 commit b6c264c

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed

src/utils/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub use vmm_sys_util::{
1414
pub mod arg_parser;
1515
pub mod byte_order;
1616
pub mod net;
17+
pub mod ring_buffer;
1718
pub mod signal;
1819
pub mod sm;
1920
pub mod time;

src/utils/src/ring_buffer.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
#[derive(Debug, Default, Clone)]
5+
pub struct RingBuffer<T: std::fmt::Debug + Default + Clone> {
6+
pub items: Box<[T]>,
7+
pub start: usize,
8+
pub len: usize,
9+
}
10+
11+
impl<T: std::fmt::Debug + Default + Clone> RingBuffer<T> {
12+
/// New with zero size
13+
pub fn new() -> Self {
14+
Self {
15+
items: Box::new([]),
16+
start: 0,
17+
len: 0,
18+
}
19+
}
20+
21+
/// New with specified size
22+
pub fn new_with_size(size: usize) -> Self {
23+
Self {
24+
items: vec![T::default(); size].into_boxed_slice(),
25+
start: 0,
26+
len: 0,
27+
}
28+
}
29+
30+
/// Get number of items in the buffer
31+
pub fn len(&self) -> usize {
32+
self.len
33+
}
34+
35+
/// Check if ring is empty
36+
pub fn is_empty(&self) -> bool {
37+
self.len == 0
38+
}
39+
40+
/// Check if ring is full
41+
pub fn is_full(&self) -> bool {
42+
self.len == self.items.len()
43+
}
44+
45+
/// Push new item to the end of the ring and increases
46+
/// the length.
47+
/// If there is no space for it, nothing will happen.
48+
pub fn push(&mut self, item: T) {
49+
if !self.is_full() {
50+
let index = (self.start + self.len) % self.items.len();
51+
self.items[index] = item;
52+
self.len += 1;
53+
}
54+
}
55+
56+
/// Return next item that will be written to and increases
57+
/// the length.
58+
/// If ring is full returns None.
59+
pub fn next_available(&mut self) -> Option<&mut T> {
60+
if self.is_full() {
61+
None
62+
} else {
63+
let index = (self.start + self.len) % self.items.len();
64+
self.len += 1;
65+
Some(&mut self.items[index])
66+
}
67+
}
68+
69+
/// Pop item from the from of the ring.
70+
/// If ring is empty returns None.
71+
pub fn pop_front(&mut self) -> Option<&mut T> {
72+
if self.is_empty() {
73+
None
74+
} else {
75+
let index = self.start;
76+
self.start += 1;
77+
self.start %= self.items.len();
78+
self.len -= 1;
79+
Some(&mut self.items[index])
80+
}
81+
}
82+
}
83+
84+
#[cfg(test)]
85+
pub mod tests {
86+
use super::*;
87+
88+
#[test]
89+
fn test_new() {
90+
let a = RingBuffer::<u8>::new();
91+
assert_eq!(a.items.len(), 0);
92+
assert_eq!(a.start, 0);
93+
assert_eq!(a.len, 0);
94+
assert!(a.is_empty());
95+
assert!(a.is_full());
96+
97+
let a = RingBuffer::<u8>::new_with_size(69);
98+
assert_eq!(a.items.len(), 69);
99+
assert_eq!(a.start, 0);
100+
assert_eq!(a.len, 0);
101+
assert!(a.is_empty());
102+
assert!(!a.is_full());
103+
}
104+
105+
#[test]
106+
fn test_push() {
107+
let mut a = RingBuffer::<u8>::new_with_size(4);
108+
109+
a.push(0);
110+
assert!(!a.is_empty());
111+
assert!(!a.is_full());
112+
113+
a.push(1);
114+
assert!(!a.is_empty());
115+
assert!(!a.is_full());
116+
117+
a.push(2);
118+
assert!(!a.is_empty());
119+
assert!(!a.is_full());
120+
121+
a.push(3);
122+
assert!(!a.is_empty());
123+
assert!(a.is_full());
124+
125+
assert_eq!(a.items.as_ref(), &[0, 1, 2, 3]);
126+
127+
a.push(4);
128+
assert!(!a.is_empty());
129+
assert!(a.is_full());
130+
131+
assert_eq!(a.items.as_ref(), &[0, 1, 2, 3]);
132+
}
133+
134+
#[test]
135+
fn test_next_available() {
136+
let mut a = RingBuffer::<u8>::new_with_size(4);
137+
assert!(a.is_empty());
138+
assert!(!a.is_full());
139+
140+
*a.next_available().unwrap() = 0;
141+
assert!(!a.is_empty());
142+
assert!(!a.is_full());
143+
144+
*a.next_available().unwrap() = 1;
145+
assert!(!a.is_empty());
146+
assert!(!a.is_full());
147+
148+
*a.next_available().unwrap() = 2;
149+
assert!(!a.is_empty());
150+
assert!(!a.is_full());
151+
152+
*a.next_available().unwrap() = 3;
153+
assert!(!a.is_empty());
154+
assert!(a.is_full());
155+
156+
assert_eq!(a.items.as_ref(), &[0, 1, 2, 3]);
157+
158+
assert!(a.next_available().is_none());
159+
160+
assert_eq!(a.items.as_ref(), &[0, 1, 2, 3]);
161+
}
162+
163+
#[test]
164+
fn test_pop_front() {
165+
let mut a = RingBuffer::<u8>::new_with_size(4);
166+
a.push(0);
167+
a.push(1);
168+
a.push(2);
169+
a.push(3);
170+
assert!(!a.is_empty());
171+
assert!(a.is_full());
172+
173+
assert_eq!(*a.pop_front().unwrap(), 0);
174+
assert!(!a.is_empty());
175+
assert!(!a.is_full());
176+
177+
assert_eq!(*a.pop_front().unwrap(), 1);
178+
assert!(!a.is_empty());
179+
assert!(!a.is_full());
180+
181+
assert_eq!(*a.pop_front().unwrap(), 2);
182+
assert!(!a.is_empty());
183+
assert!(!a.is_full());
184+
185+
assert_eq!(*a.pop_front().unwrap(), 3);
186+
assert!(a.is_empty());
187+
assert!(!a.is_full());
188+
189+
assert!(a.pop_front().is_none());
190+
assert!(a.is_empty());
191+
assert!(!a.is_full());
192+
}
193+
}

0 commit comments

Comments
 (0)