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}