sealy/
memory.rs

1use crate::{error::Result, try_seal};
2use std::{
3	ffi::c_void,
4	ptr::null_mut,
5	sync::atomic::{AtomicPtr, Ordering},
6};
7
8use crate::bindgen;
9
10/// Memory pool handle for SEAL.
11///
12/// The purpose of a custom memory management is to save allocation/deallocation overhead.
13/// SEAL has a significant runtime overhead caused by memory allocation/deallocation due to
14/// the large amount of memory space required by SEAL. The custom memory pool is designed to
15/// reduce this overhead by providing a way to allocate memory in advance and reuse it.
16///
17/// In the FFI, the memory pool is read-only and cannot be modified. The memory pool is
18/// initialized by the library and is used by the library to allocate memory. The methods
19/// provided by this crate are just to check the pool's health and status.
20#[derive(Debug)]
21pub struct MemoryPool {
22	/// A pointer to the underlying SEAL memory pool.
23	pub(crate) handle: AtomicPtr<c_void>,
24}
25
26impl MemoryPool {
27	/// Creates an empty SEAL memory pool.
28	pub fn new() -> Result<Self> {
29		let mut handle: *mut c_void = null_mut();
30		let clear_on_destruction = true;
31
32		try_seal!(unsafe { bindgen::MemoryPoolHandle_New(clear_on_destruction, &mut handle) })?;
33
34		Ok(MemoryPool {
35			handle: AtomicPtr::new(handle),
36		})
37	}
38
39	/// Returns the number of allocations in the pool.
40	pub fn pool_count(&self) -> Result<u64> {
41		let mut count: u64 = 0;
42
43		try_seal!(unsafe { bindgen::MemoryPoolHandle_PoolCount(self.get_handle(), &mut count) })?;
44
45		Ok(count)
46	}
47
48	/// Returns the number of bytes allocated in the pool.
49	pub fn pool_allocated_byte_count(&self) -> Result<u64> {
50		let mut count: u64 = 0;
51
52		try_seal!(unsafe {
53			bindgen::MemoryPoolHandle_AllocByteCount(self.get_handle(), &mut count)
54		})?;
55
56		Ok(count)
57	}
58
59	/// Returns the number of bytes used in the pool.
60	pub fn pool_used_byte_count(&self) -> Result<i64> {
61		let mut count: i64 = 0;
62
63		try_seal!(unsafe { bindgen::MemoryPoolHandle_UseCount(self.get_handle(), &mut count) })?;
64
65		Ok(count)
66	}
67
68	/// Returns true if the pool is initialized.
69	pub fn is_initialized(&self) -> Result<bool> {
70		let mut result: bool = false;
71
72		try_seal!(unsafe {
73			bindgen::MemoryPoolHandle_IsInitialized(self.get_handle(), &mut result)
74		})?;
75
76		Ok(result)
77	}
78
79	/// Returns handle to the underlying SEAL object.
80	pub(crate) unsafe fn get_handle(&self) -> *mut c_void {
81		self.handle.load(Ordering::SeqCst)
82	}
83}
84
85impl Drop for MemoryPool {
86	fn drop(&mut self) {
87		if let Err(err) = try_seal!(unsafe { bindgen::MemoryPoolHandle_Destroy(self.get_handle()) })
88		{
89			panic!("Failed to destroy memory pool: {:?}", err);
90		}
91	}
92}
93
94#[cfg(test)]
95mod tests {
96	use crate::*;
97
98	#[test]
99	fn can_create_and_destroy_memory_pool() {
100		let memory_pool = MemoryPool::new().unwrap();
101		assert!(memory_pool.is_initialized().unwrap());
102		std::mem::drop(memory_pool);
103	}
104
105	#[test]
106	fn can_get_pool_count() {
107		let memory_pool = MemoryPool::new().unwrap();
108		let count = memory_pool.pool_count().unwrap();
109		let is_initialized = memory_pool.is_initialized().unwrap();
110		assert_eq!(count, 0);
111		assert!(is_initialized);
112	}
113}