beamer_core/
sysex_pool.rs1pub struct SysExOutputPool {
11 buffers: Vec<Vec<u8>>,
13 lengths: Vec<usize>,
15 max_slots: usize,
17 max_buffer_size: usize,
19 next_slot: usize,
21 overflowed: bool,
23 #[cfg(feature = "sysex-heap-fallback")]
25 fallback: Vec<Vec<u8>>,
26}
27
28impl SysExOutputPool {
29 pub const DEFAULT_SLOTS: usize = 16;
31 pub const DEFAULT_BUFFER_SIZE: usize = 512;
33
34 pub fn new() -> Self {
36 Self::with_capacity(Self::DEFAULT_SLOTS, Self::DEFAULT_BUFFER_SIZE)
37 }
38
39 pub fn with_capacity(slots: usize, buffer_size: usize) -> Self {
43 let mut buffers = Vec::with_capacity(slots);
44 for _ in 0..slots {
45 buffers.push(vec![0u8; buffer_size]);
46 }
47 let lengths = vec![0usize; slots];
48
49 Self {
50 buffers,
51 lengths,
52 max_slots: slots,
53 max_buffer_size: buffer_size,
54 next_slot: 0,
55 overflowed: false,
56 #[cfg(feature = "sysex-heap-fallback")]
57 fallback: Vec::new(),
58 }
59 }
60
61 #[inline]
66 pub fn clear(&mut self) {
67 self.next_slot = 0;
68 self.overflowed = false;
69 }
70
71 pub fn allocate(&mut self, data: &[u8]) -> Option<(*const u8, usize)> {
80 if self.next_slot >= self.max_slots {
81 self.overflowed = true;
82
83 #[cfg(feature = "sysex-heap-fallback")]
84 {
85 let copy_len = data.len().min(self.max_buffer_size);
86 self.fallback.push(data[..copy_len].to_vec());
87 }
88
89 return None;
90 }
91
92 let slot = self.next_slot;
93 self.next_slot += 1;
94
95 let copy_len = data.len().min(self.max_buffer_size);
96 self.buffers[slot][..copy_len].copy_from_slice(&data[..copy_len]);
97 self.lengths[slot] = copy_len;
98
99 Some((self.buffers[slot].as_ptr(), copy_len))
100 }
101
102 pub fn allocate_slice(&mut self, data: &[u8]) -> Option<&[u8]> {
106 if self.next_slot >= self.max_slots {
107 self.overflowed = true;
108
109 #[cfg(feature = "sysex-heap-fallback")]
110 {
111 let copy_len = data.len().min(self.max_buffer_size);
112 self.fallback.push(data[..copy_len].to_vec());
113 }
114
115 return None;
116 }
117
118 let slot = self.next_slot;
119 self.next_slot += 1;
120
121 let copy_len = data.len().min(self.max_buffer_size);
122 self.buffers[slot][..copy_len].copy_from_slice(&data[..copy_len]);
123 self.lengths[slot] = copy_len;
124
125 Some(&self.buffers[slot][..copy_len])
126 }
127
128 #[inline]
130 pub fn has_overflowed(&self) -> bool {
131 self.overflowed
132 }
133
134 #[inline]
136 pub fn capacity(&self) -> usize {
137 self.max_slots
138 }
139
140 #[inline]
142 pub fn used(&self) -> usize {
143 self.next_slot
144 }
145
146 #[cfg(feature = "sysex-heap-fallback")]
148 #[inline]
149 pub fn has_fallback(&self) -> bool {
150 !self.fallback.is_empty()
151 }
152
153 #[cfg(feature = "sysex-heap-fallback")]
157 #[inline]
158 pub fn take_fallback(&mut self) -> Vec<Vec<u8>> {
159 std::mem::take(&mut self.fallback)
160 }
161}
162
163impl Default for SysExOutputPool {
164 fn default() -> Self {
165 Self::new()
166 }
167}
168
169#[cfg(test)]
170#[allow(clippy::undocumented_unsafe_blocks)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn test_new_pool() {
176 let pool = SysExOutputPool::new();
177 assert_eq!(pool.capacity(), SysExOutputPool::DEFAULT_SLOTS);
178 assert_eq!(pool.used(), 0);
179 assert!(!pool.has_overflowed());
180 }
181
182 #[test]
183 fn test_allocate() {
184 let mut pool = SysExOutputPool::with_capacity(2, 64);
185 let data = [0xF0, 0x41, 0x10, 0xF7];
186
187 let result = pool.allocate(&data);
188 assert!(result.is_some());
189 assert_eq!(pool.used(), 1);
190
191 let (ptr, len) = result.unwrap();
192 assert_eq!(len, 4);
193 let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
194 assert_eq!(slice, &data);
195 }
196
197 #[test]
198 fn test_allocate_slice() {
199 let mut pool = SysExOutputPool::with_capacity(2, 64);
200 let data = [0xF0, 0x41, 0x10, 0xF7];
201
202 let result = pool.allocate_slice(&data);
203 assert!(result.is_some());
204 assert_eq!(result.unwrap(), &data);
205 assert_eq!(pool.used(), 1);
206 }
207
208 #[test]
209 fn test_overflow() {
210 let mut pool = SysExOutputPool::with_capacity(1, 64);
211 let data = [0xF0, 0xF7];
212
213 assert!(pool.allocate(&data).is_some());
214 assert!(!pool.has_overflowed());
215
216 assert!(pool.allocate(&data).is_none());
217 assert!(pool.has_overflowed());
218 }
219
220 #[test]
221 fn test_clear() {
222 let mut pool = SysExOutputPool::with_capacity(1, 64);
223 let data = [0xF0, 0xF7];
224
225 pool.allocate(&data);
226 pool.allocate(&data); assert!(pool.has_overflowed());
228 assert_eq!(pool.used(), 1);
229
230 pool.clear();
231 assert!(!pool.has_overflowed());
232 assert_eq!(pool.used(), 0);
233 }
234
235 #[test]
236 fn test_truncation() {
237 let mut pool = SysExOutputPool::with_capacity(1, 4);
238 let data = [0xF0, 0x41, 0x10, 0x42, 0x00, 0xF7]; let result = pool.allocate_slice(&data);
241 assert!(result.is_some());
242 assert_eq!(result.unwrap().len(), 4); }
244}