1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! Contains simple slab data structures for use with the TX and RX rings
use crate::Packet;
/// A fixed size buffer of packets
pub trait Slab {
/// The number of free slots available
fn available(&self) -> usize;
/// The number of occupied slots
fn len(&self) -> usize;
/// True if the slab is empty
fn is_empty(&self) -> bool;
/// Pushes a packet to the slab, returning the packet if the slab is at capacity
fn push_front(&mut self, packet: Packet) -> Option<Packet>;
/// Pops the back packet if any
fn pop_back(&mut self) -> Option<Packet>;
}
// A heap allocated slab, using [`std::collections::VecDequeue`]
///
/// This is allocated on the heap, but will _not_ grow, and is intended to be
/// allocated once before entering an I/O loop
pub struct HeapSlab {
vd: std::collections::VecDeque<Packet>,
}
impl HeapSlab {
/// Allocates a new [`Self`] with the maximum specified capacity
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
vd: std::collections::VecDeque::with_capacity(capacity),
}
}
}
impl Slab for HeapSlab {
/// The number of packets in the slab
#[inline]
fn len(&self) -> usize {
self.vd.len()
}
/// True if the slab is empty
#[inline]
fn is_empty(&self) -> bool {
self.vd.is_empty()
}
/// The number of packets that can be pushed to the slab
#[inline]
fn available(&self) -> usize {
self.vd.capacity() - self.vd.len()
}
/// Pops the front packet if any
#[inline]
fn pop_back(&mut self) -> Option<Packet> {
self.vd.pop_back()
}
/// Pushes a packet to the front, returning `Some` if the slab is at capacity
#[inline]
fn push_front(&mut self, item: Packet) -> Option<Packet> {
if self.available() > 0 {
self.vd.push_front(item);
None
} else {
Some(item)
}
}
}
struct AssertPowerOf2<const N: usize>;
impl<const N: usize> AssertPowerOf2<N> {
const OK: () = assert!(usize::is_power_of_two(N), "must be a power of 2");
}
#[doc(hidden)]
pub const fn assert_power_of_2<const N: usize>() {
let () = AssertPowerOf2::<N>::OK;
}
#[cfg_attr(debug_assertions, macro_export, doc(hidden))]
macro_rules! slab {
($name:ident, $int:ty) => {
/// A stack allocated, fixed size, ring buffer
pub struct $name<const N: usize> {
ring: [$crate::Packet; N],
read: $int,
write: $int,
}
impl<const N: usize> $name<N> {
/// Creates a new slab, `N` must be a power of 2
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
$crate::slab::assert_power_of_2::<N>();
Self {
// SAFETY: Packet is just a POD
ring: unsafe { std::mem::zeroed() },
read: 0,
write: 0,
}
}
}
impl<const N: usize> $crate::slab::Slab for $name<N> {
/// The current number of packets in the slab
#[inline]
fn len(&self) -> usize {
if self.write >= self.read {
(self.write - self.read) as _
} else {
<$int>::MAX as usize - self.read as usize + self.write as usize + 1
}
}
/// True if the slab is empty
#[inline]
fn is_empty(&self) -> bool {
self.write == self.read
}
/// The number of packets that can be pushed to the slab
#[inline]
fn available(&self) -> usize {
N - self.len()
}
/// Pops the back packet if any
#[inline]
fn pop_back(&mut self) -> Option<$crate::Packet> {
if self.is_empty() {
return None;
}
let index = self.read as usize % N;
self.read = self.read.wrapping_add(1);
Some(self.ring[index].__inner_copy())
}
/// Pushes a packet to the front, returning `Some` if the slab is at capacity
#[inline]
fn push_front(&mut self, item: $crate::Packet) -> Option<$crate::Packet> {
if self.available() > 0 {
let index = self.write as usize % N;
self.write = self.write.wrapping_add(1);
self.ring[index] = item;
None
} else {
Some(item)
}
}
}
};
}
slab!(StackSlab, usize);