safer_ring/advanced/
buffer_group.rs

1//! Buffer group management for provided buffers.
2
3use crate::error::{Result, SaferRingError};
4use std::collections::VecDeque;
5
6/// Buffer selection group for managing provided buffers.
7///
8/// Allows the kernel to select buffers from a pre-registered pool,
9/// enabling zero-copy operations and reducing buffer management overhead.
10#[derive(Debug)]
11pub struct BufferGroup {
12    /// Group ID for this buffer set
13    pub group_id: u16,
14    /// Number of buffers in the group
15    pub buffer_count: u32,
16    /// Size of each buffer in bytes
17    pub buffer_size: u32,
18    /// Pre-allocated buffer storage
19    #[cfg(target_os = "linux")]
20    #[allow(dead_code)] // Will be used when buffer selection is implemented
21    buffers: Vec<Box<[u8]>>,
22    /// Available buffer indices - using VecDeque for O(1) operations
23    available_buffers: VecDeque<u16>,
24}
25
26impl BufferGroup {
27    /// Create a new buffer group with the specified parameters.
28    ///
29    /// # Arguments
30    ///
31    /// * `group_id` - Unique identifier for this buffer group
32    /// * `buffer_count` - Number of buffers to allocate
33    /// * `buffer_size` - Size of each buffer in bytes
34    ///
35    /// # Errors
36    ///
37    /// Returns an error if buffer allocation fails or parameters are invalid.
38    ///
39    /// # Examples
40    ///
41    /// ```rust,no_run
42    /// use safer_ring::advanced::BufferGroup;
43    ///
44    /// let group = BufferGroup::new(1, 100, 4096)?;
45    /// # Ok::<(), safer_ring::error::SaferRingError>(())
46    /// ```
47    pub fn new(group_id: u16, buffer_count: u32, buffer_size: u32) -> Result<Self> {
48        if buffer_count == 0 {
49            return Err(SaferRingError::Io(std::io::Error::new(
50                std::io::ErrorKind::InvalidInput,
51                "Buffer count must be greater than 0",
52            )));
53        }
54
55        if buffer_size == 0 {
56            return Err(SaferRingError::Io(std::io::Error::new(
57                std::io::ErrorKind::InvalidInput,
58                "Buffer size must be greater than 0",
59            )));
60        }
61
62        // Pre-allocate all buffers to avoid runtime allocation overhead
63        #[cfg(target_os = "linux")]
64        let buffers = {
65            let mut buffers = Vec::with_capacity(buffer_count as usize);
66            for _ in 0..buffer_count {
67                // Use boxed slice for stable memory layout
68                buffers.push(vec![0u8; buffer_size as usize].into_boxed_slice());
69            }
70            buffers
71        };
72
73        // Initialize all buffer indices as available
74        let available_buffers = (0..buffer_count as u16).collect();
75
76        Ok(Self {
77            group_id,
78            buffer_count,
79            buffer_size,
80            #[cfg(target_os = "linux")]
81            buffers,
82            available_buffers,
83        })
84    }
85
86    /// Get the next available buffer index.
87    ///
88    /// Returns `None` if no buffers are available.
89    #[inline]
90    pub fn get_buffer(&mut self) -> Option<u16> {
91        self.available_buffers.pop_front()
92    }
93
94    /// Return a buffer to the available pool.
95    ///
96    /// # Arguments
97    ///
98    /// * `buffer_id` - The buffer index to return
99    ///
100    /// # Panics
101    ///
102    /// Panics in debug mode if `buffer_id` is invalid.
103    #[inline]
104    pub fn return_buffer(&mut self, buffer_id: u16) {
105        debug_assert!(
106            buffer_id < self.buffer_count as u16,
107            "Invalid buffer ID: {} >= {}",
108            buffer_id,
109            self.buffer_count
110        );
111
112        // Only add valid buffer IDs to prevent corruption
113        if buffer_id < self.buffer_count as u16 {
114            self.available_buffers.push_back(buffer_id);
115        }
116    }
117
118    /// Get the number of available buffers.
119    #[inline]
120    pub fn available_count(&self) -> usize {
121        self.available_buffers.len()
122    }
123
124    /// Check if any buffers are available.
125    #[inline]
126    pub fn has_available(&self) -> bool {
127        !self.available_buffers.is_empty()
128    }
129
130    /// Get the utilization ratio (0.0 to 1.0).
131    ///
132    /// Returns the fraction of buffers currently in use.
133    #[inline]
134    pub fn utilization(&self) -> f64 {
135        let in_use = self.buffer_count as usize - self.available_buffers.len();
136        in_use as f64 / self.buffer_count as f64
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn test_buffer_group_creation() {
146        let group = BufferGroup::new(1, 10, 4096).unwrap();
147
148        assert_eq!(group.group_id, 1);
149        assert_eq!(group.buffer_count, 10);
150        assert_eq!(group.buffer_size, 4096);
151        assert_eq!(group.available_count(), 10);
152        assert!(group.has_available());
153        assert_eq!(group.utilization(), 0.0);
154    }
155
156    #[test]
157    fn test_buffer_allocation() {
158        let mut group = BufferGroup::new(1, 5, 1024).unwrap();
159
160        // Get all buffers
161        let mut buffer_ids = Vec::new();
162        for _ in 0..5 {
163            let id = group.get_buffer().unwrap();
164            buffer_ids.push(id);
165        }
166
167        assert_eq!(group.available_count(), 0);
168        assert!(!group.has_available());
169        assert_eq!(group.utilization(), 1.0);
170        assert!(group.get_buffer().is_none());
171
172        // Return buffers
173        for id in buffer_ids {
174            group.return_buffer(id);
175        }
176
177        assert_eq!(group.available_count(), 5);
178        assert!(group.has_available());
179        assert_eq!(group.utilization(), 0.0);
180    }
181
182    #[test]
183    fn test_invalid_parameters() {
184        assert!(BufferGroup::new(1, 0, 4096).is_err());
185        assert!(BufferGroup::new(1, 10, 0).is_err());
186    }
187}