Skip to main content

leodos_libcfs/cfe/es/
pool.rs

1//! Safe, idiomatic wrappers for the CFE Executive Services Memory Pool API.
2//!
3//! This module provides a `MemPool` handle and a `PoolBuffer` RAII guard
4//! to ensure that memory allocated from a pool is always returned, preventing
5//! memory leaks.
6
7use crate::error::{CfsError, Result};
8use crate::ffi;
9use crate::status::check;
10use core::marker::PhantomData;
11use core::mem::MaybeUninit;
12use core::ops::{Deref, DerefMut};
13use core::slice;
14
15// Add these new struct definitions before the `MemPool` struct
16/// Statistics about a specific block size within a memory pool.
17#[derive(Debug, Clone, Copy)]
18pub struct BlockStats {
19    /// Number of bytes in each of these blocks.
20    pub block_size: u32,
21    /// Number of memory blocks of this size created.
22    pub num_created: u32,
23    /// Number of memory blocks of this size that are free.
24    pub num_free: u32,
25}
26
27impl From<ffi::CFE_ES_BlockStats_t> for BlockStats {
28    fn from(stats: ffi::CFE_ES_BlockStats_t) -> Self {
29        Self {
30            block_size: stats.BlockSize,
31            num_created: stats.NumCreated,
32            num_free: stats.NumFree,
33        }
34    }
35}
36
37/// Statistics about a cFE memory pool.
38#[derive(Debug, Clone, Copy)]
39pub struct MemPoolStats {
40    /// Total size of the memory pool in bytes.
41    pub pool_size: u32,
42    /// Number of times a memory block has been allocated.
43    pub num_blocks_requested: u32,
44    /// Number of errors detected when freeing a memory block.
45    pub check_err_ctr: u32,
46    /// Number of bytes never allocated to a block.
47    pub num_free_bytes: u32,
48    /// Statistics for each available block size.
49    pub block_stats: [BlockStats; ffi::CFE_MISSION_ES_POOL_MAX_BUCKETS as usize],
50}
51
52impl From<ffi::CFE_ES_MemPoolStats_t> for MemPoolStats {
53    fn from(stats: ffi::CFE_ES_MemPoolStats_t) -> Self {
54        let mut block_stats = [BlockStats {
55            block_size: 0,
56            num_created: 0,
57            num_free: 0,
58        }; ffi::CFE_MISSION_ES_POOL_MAX_BUCKETS as usize];
59        for i in 0..block_stats.len() {
60            block_stats[i] = stats.BlockStats[i].into();
61        }
62
63        Self {
64            pool_size: stats.PoolSize,
65            num_blocks_requested: stats.NumBlocksRequested,
66            check_err_ctr: stats.CheckErrCtr,
67            num_free_bytes: stats.NumFreeBytes,
68            block_stats,
69        }
70    }
71}
72
73/// A handle to a cFE Executive Services memory pool.
74///
75/// This struct is an RAII wrapper that ensures the underlying pool is deleted
76/// when it goes out of scope. The memory for the pool itself must have a
77/// `'static` lifetime.
78#[derive(Debug)]
79pub struct MemPool {
80    handle: ffi::CFE_ES_MemHandle_t,
81}
82
83impl MemPool {
84    /// Creates a new memory pool from a statically allocated memory region.
85    ///
86    /// This uses the default cFE block sizes for the pool.
87    ///
88    /// The pool size must be an integral number of 32-bit words, the
89    /// start address must be 32-bit aligned, and 168 bytes are
90    /// reserved for internal bookkeeping.
91    ///
92    /// # Arguments
93    /// * `memory`: A mutable static byte slice to be used as the pool's memory.
94    /// * `use_mutex`: If `true`, access to the pool will be protected by a mutex.
95    ///
96    /// # Errors
97    /// Returns an error if the memory pool cannot be created, e.g., due to an
98    /// invalid memory pointer or size.
99    pub fn new(memory: &'static mut [u8], use_mutex: bool) -> Result<Self> {
100        let mut handle = ffi::CFE_ES_MEMHANDLE_UNDEFINED;
101        let status = if use_mutex {
102            unsafe {
103                ffi::CFE_ES_PoolCreate(&mut handle, memory.as_mut_ptr() as *mut _, memory.len())
104            }
105        } else {
106            unsafe {
107                ffi::CFE_ES_PoolCreateNoSem(
108                    &mut handle,
109                    memory.as_mut_ptr() as *mut _,
110                    memory.len(),
111                )
112            }
113        };
114        check(status)?;
115        Ok(Self { handle })
116    }
117
118    /// Creates a new memory pool with user-defined block sizes.
119    ///
120    /// # Arguments
121    /// * `memory`: A mutable static byte slice for the pool's memory.
122    /// * `use_mutex`: If `true`, access to the pool is protected by a mutex.
123    /// * `block_sizes`: A slice of `usize` defining the bucket sizes for the pool.
124    ///
125    /// # Errors
126    /// Returns an error if the pool cannot be created, e.g., due to an invalid
127    /// argument, too many block sizes, or an external resource failure (like
128    /// failing to create a mutex).
129    pub fn new_ex(
130        memory: &'static mut [u8],
131        use_mutex: bool,
132        block_sizes: &[usize],
133    ) -> Result<Self> {
134        let mut handle = ffi::CFE_ES_MEMHANDLE_UNDEFINED;
135        check(unsafe {
136            ffi::CFE_ES_PoolCreateEx(
137                &mut handle,
138                memory.as_mut_ptr() as *mut _,
139                memory.len(),
140                block_sizes.len() as u16,
141                block_sizes.as_ptr(),
142                use_mutex,
143            )
144        })?;
145        Ok(Self { handle })
146    }
147
148    /// Allocates a buffer of at least `size` bytes from the pool.
149    ///
150    /// The actual allocation is at least 12 bytes larger than
151    /// requested (internal block header overhead). The returned
152    /// buffer size is rounded up to the next available block size
153    /// in the pool.
154    ///
155    /// Returns a `PoolBuffer` guard. When this guard is dropped,
156    /// the memory is automatically returned to the pool.
157    ///
158    /// # Errors
159    /// Returns an error if a buffer cannot be allocated, for example, if the pool
160    /// is out of memory or the requested size is larger than the largest available
161    /// block size.
162    pub fn get_buf(&self, size: usize) -> Result<PoolBuffer<'_>> {
163        let mut buf_ptr = core::ptr::null_mut();
164        let actual_size = unsafe { ffi::CFE_ES_GetPoolBuf(&mut buf_ptr, self.handle, size) };
165        if actual_size < 0 {
166            return Err(CfsError::from(actual_size));
167        }
168
169        Ok(PoolBuffer {
170            ptr: buf_ptr,
171            size: actual_size as usize,
172            pool_handle: self.handle,
173            _phantom: PhantomData,
174        })
175    }
176
177    /// Retrieves statistics about this memory pool.
178    ///
179    /// # Errors
180    /// Returns an error if the pool handle is invalid or the underlying CFE
181    /// call fails.
182    pub fn stats(&self) -> Result<MemPoolStats> {
183        let mut stats = MaybeUninit::uninit();
184        check(unsafe { ffi::CFE_ES_GetMemPoolStats(stats.as_mut_ptr(), self.handle) })?;
185        Ok(unsafe { stats.assume_init() }.into())
186    }
187
188    /// Gets information about a buffer previously allocated from this pool.
189    ///
190    /// Returns the allocated size of the buffer.
191    ///
192    /// # Errors
193    /// Returns an error if the pool handle is invalid or the provided buffer
194    /// pointer does not belong to this pool.
195    pub fn get_buf_info(&self, buf: &PoolBuffer) -> Result<usize> {
196        let status = unsafe { ffi::CFE_ES_GetPoolBufInfo(self.handle, buf.ptr) };
197        if status < 0 {
198            Err(CfsError::from(status))
199        } else {
200            Ok(status as usize)
201        }
202    }
203}
204
205impl Drop for MemPool {
206    /// Deletes the memory pool when the `MemPool` object goes out of scope.
207    fn drop(&mut self) {
208        let _ = unsafe { ffi::CFE_ES_PoolDelete(self.handle) };
209    }
210}
211
212/// An RAII guard for a buffer allocated from a `MemPool`.
213///
214/// When this struct is dropped, its memory is automatically released back to the
215/// originating pool by calling `CFE_ES_PutPoolBuf`. It provides safe `&[u8]` and
216/// `&mut [u8]` views into the buffer's memory.
217#[derive(Debug)]
218#[must_use = "if unused the buffer will be immediately returned to the pool"]
219pub struct PoolBuffer<'a> {
220    ptr: ffi::CFE_ES_MemPoolBuf_t,
221    size: usize,
222    pool_handle: ffi::CFE_ES_MemHandle_t,
223    _phantom: PhantomData<&'a MemPool>,
224}
225
226impl<'a> Deref for PoolBuffer<'a> {
227    type Target = [u8];
228    fn deref(&self) -> &Self::Target {
229        unsafe { slice::from_raw_parts(self.ptr as *const u8, self.size) }
230    }
231}
232
233impl<'a> DerefMut for PoolBuffer<'a> {
234    fn deref_mut(&mut self) -> &mut Self::Target {
235        unsafe { slice::from_raw_parts_mut(self.ptr as *mut u8, self.size) }
236    }
237}
238
239impl<'a> Drop for PoolBuffer<'a> {
240    /// Automatically releases the buffer back to the memory pool.
241    fn drop(&mut self) {
242        let _ = unsafe { ffi::CFE_ES_PutPoolBuf(self.pool_handle, self.ptr) };
243    }
244}