kizzasi_io/
zerocopy.rs

1//! Zero-copy buffer implementations
2//!
3//! Provides efficient buffer types that minimize memory allocations
4//! and copies for high-performance streaming applications.
5
6use bytes::{Bytes, BytesMut};
7use scirs2_core::ndarray::Array1;
8use std::sync::Arc;
9
10/// Zero-copy signal buffer using Arc for shared ownership
11///
12/// Allows multiple readers to access the same buffer without copying.
13#[derive(Debug, Clone)]
14pub struct SharedSignalBuffer {
15    /// Shared buffer data
16    data: Arc<Vec<f32>>,
17    /// Start offset
18    offset: usize,
19    /// Length
20    len: usize,
21}
22
23impl SharedSignalBuffer {
24    /// Create a new shared buffer from a vector
25    pub fn new(data: Vec<f32>) -> Self {
26        let len = data.len();
27        Self {
28            data: Arc::new(data),
29            offset: 0,
30            len,
31        }
32    }
33
34    /// Create from an Arc
35    pub fn from_arc(data: Arc<Vec<f32>>) -> Self {
36        let len = data.len();
37        Self {
38            data,
39            offset: 0,
40            len,
41        }
42    }
43
44    /// Create a slice view of this buffer (zero-copy)
45    pub fn slice(&self, start: usize, end: usize) -> Self {
46        assert!(start <= end && end <= self.len);
47        Self {
48            data: Arc::clone(&self.data),
49            offset: self.offset + start,
50            len: end - start,
51        }
52    }
53
54    /// Get a reference to the data slice
55    pub fn as_slice(&self) -> &[f32] {
56        &self.data[self.offset..self.offset + self.len]
57    }
58
59    /// Get the length
60    pub fn len(&self) -> usize {
61        self.len
62    }
63
64    /// Check if empty
65    pub fn is_empty(&self) -> bool {
66        self.len == 0
67    }
68
69    /// Convert to Array1 (copies data)
70    pub fn to_array(&self) -> Array1<f32> {
71        Array1::from_vec(self.as_slice().to_vec())
72    }
73
74    /// Get the number of strong references
75    pub fn ref_count(&self) -> usize {
76        Arc::strong_count(&self.data)
77    }
78}
79
80/// Zero-copy byte buffer for raw data streams
81#[derive(Debug, Clone)]
82pub struct ZeroCopyBuffer {
83    /// Underlying bytes
84    data: Bytes,
85}
86
87impl ZeroCopyBuffer {
88    /// Create a new buffer from bytes
89    pub fn new(data: Bytes) -> Self {
90        Self { data }
91    }
92
93    /// Create from a vector (takes ownership)
94    pub fn from_vec(vec: Vec<u8>) -> Self {
95        Self {
96            data: Bytes::from(vec),
97        }
98    }
99
100    /// Create from a slice (copies)
101    pub fn from_slice(slice: &[u8]) -> Self {
102        Self {
103            data: Bytes::copy_from_slice(slice),
104        }
105    }
106
107    /// Get a slice of this buffer (zero-copy)
108    pub fn slice(&self, range: impl std::ops::RangeBounds<usize>) -> Self {
109        Self {
110            data: self.data.slice(range),
111        }
112    }
113
114    /// Get the underlying bytes
115    pub fn as_bytes(&self) -> &Bytes {
116        &self.data
117    }
118
119    /// Get length
120    pub fn len(&self) -> usize {
121        self.data.len()
122    }
123
124    /// Check if empty
125    pub fn is_empty(&self) -> bool {
126        self.data.is_empty()
127    }
128
129    /// Convert to f32 samples (assumes IEEE 754 encoding)
130    pub fn to_f32_samples(&self) -> Vec<f32> {
131        let byte_slice = self.data.as_ref();
132        let sample_count = byte_slice.len() / 4;
133        let mut samples = Vec::with_capacity(sample_count);
134
135        for i in 0..sample_count {
136            let offset = i * 4;
137            let bytes = [
138                byte_slice[offset],
139                byte_slice[offset + 1],
140                byte_slice[offset + 2],
141                byte_slice[offset + 3],
142            ];
143            samples.push(f32::from_le_bytes(bytes));
144        }
145
146        samples
147    }
148
149    /// Convert to Array1<f32>
150    pub fn to_array(&self) -> Array1<f32> {
151        Array1::from_vec(self.to_f32_samples())
152    }
153}
154
155/// Mutable zero-copy buffer builder
156pub struct ZeroCopyBufferMut {
157    /// Mutable bytes
158    data: BytesMut,
159}
160
161impl ZeroCopyBufferMut {
162    /// Create a new mutable buffer with capacity
163    pub fn with_capacity(capacity: usize) -> Self {
164        Self {
165            data: BytesMut::with_capacity(capacity),
166        }
167    }
168
169    /// Append bytes
170    pub fn extend_from_slice(&mut self, bytes: &[u8]) {
171        self.data.extend_from_slice(bytes);
172    }
173
174    /// Append f32 sample
175    pub fn push_f32(&mut self, sample: f32) {
176        self.data.extend_from_slice(&sample.to_le_bytes());
177    }
178
179    /// Append multiple f32 samples
180    pub fn extend_f32(&mut self, samples: &[f32]) {
181        for &sample in samples {
182            self.push_f32(sample);
183        }
184    }
185
186    /// Freeze into immutable buffer
187    pub fn freeze(self) -> ZeroCopyBuffer {
188        ZeroCopyBuffer {
189            data: self.data.freeze(),
190        }
191    }
192
193    /// Get length
194    pub fn len(&self) -> usize {
195        self.data.len()
196    }
197
198    /// Check if empty
199    pub fn is_empty(&self) -> bool {
200        self.data.is_empty()
201    }
202
203    /// Get capacity
204    pub fn capacity(&self) -> usize {
205        self.data.capacity()
206    }
207}
208
209/// Pool of reusable buffers to reduce allocation overhead
210pub struct BufferPool {
211    /// Pool of available buffers
212    pool: crossbeam_queue::SegQueue<BytesMut>,
213    /// Default buffer capacity
214    capacity: usize,
215}
216
217impl BufferPool {
218    /// Create a new buffer pool with default capacity
219    pub fn new(capacity: usize) -> Self {
220        Self {
221            pool: crossbeam_queue::SegQueue::new(),
222            capacity,
223        }
224    }
225
226    /// Acquire a buffer from the pool or create a new one
227    pub fn acquire(&self) -> BytesMut {
228        self.pool
229            .pop()
230            .unwrap_or_else(|| BytesMut::with_capacity(self.capacity))
231    }
232
233    /// Return a buffer to the pool
234    pub fn release(&self, mut buffer: BytesMut) {
235        buffer.clear();
236        if buffer.capacity() >= self.capacity {
237            self.pool.push(buffer);
238        }
239        // Drop buffers that are too small
240    }
241
242    /// Get pool size (approximate)
243    pub fn len(&self) -> usize {
244        self.pool.len()
245    }
246
247    /// Check if pool is empty
248    pub fn is_empty(&self) -> bool {
249        self.pool.is_empty()
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256
257    #[test]
258    fn test_shared_signal_buffer() {
259        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
260        let buffer = SharedSignalBuffer::new(data);
261
262        assert_eq!(buffer.len(), 5);
263        assert_eq!(buffer.ref_count(), 1);
264
265        let slice = buffer.slice(1, 4);
266        assert_eq!(slice.len(), 3);
267        assert_eq!(slice.as_slice(), &[2.0, 3.0, 4.0]);
268        assert_eq!(buffer.ref_count(), 2);
269    }
270
271    #[test]
272    fn test_zerocopy_buffer() {
273        let mut builder = ZeroCopyBufferMut::with_capacity(16);
274        builder.push_f32(1.0);
275        builder.push_f32(2.0);
276        builder.push_f32(3.0);
277
278        let buffer = builder.freeze();
279        assert_eq!(buffer.len(), 12); // 3 floats * 4 bytes
280
281        let samples = buffer.to_f32_samples();
282        assert_eq!(samples, vec![1.0, 2.0, 3.0]);
283    }
284
285    #[test]
286    fn test_zerocopy_buffer_slice() {
287        let data = vec![1, 2, 3, 4, 5, 6, 7, 8];
288        let buffer = ZeroCopyBuffer::from_vec(data);
289
290        let slice = buffer.slice(2..6);
291        assert_eq!(slice.len(), 4);
292        assert_eq!(slice.as_bytes().as_ref(), &[3, 4, 5, 6]);
293    }
294
295    #[test]
296    fn test_buffer_pool() {
297        let pool = BufferPool::new(1024);
298        assert!(pool.is_empty());
299
300        let buffer1 = pool.acquire();
301        assert_eq!(buffer1.capacity(), 1024);
302
303        pool.release(buffer1);
304        assert_eq!(pool.len(), 1);
305
306        let buffer2 = pool.acquire();
307        assert_eq!(buffer2.capacity(), 1024);
308    }
309
310    #[test]
311    fn test_shared_buffer_to_array() {
312        let data = vec![1.0, 2.0, 3.0];
313        let buffer = SharedSignalBuffer::new(data);
314        let array = buffer.to_array();
315        assert_eq!(array.len(), 3);
316        assert_eq!(array[0], 1.0);
317    }
318}