use alloc::rc::Rc;
use apr_sys;
use core::mem::ManuallyDrop;
use core::ops::{Deref, DerefMut};
#[derive(Debug)]
#[repr(transparent)]
pub struct Pool<'pool> {
raw: *mut apr_sys::apr_pool_t,
_marker: core::marker::PhantomData<&'pool ()>,
}
#[cfg(feature = "pool-debug")]
#[macro_export]
macro_rules! pool_debug {
($name:ident, $doc:expr) => {
#[doc = $doc]
pub fn $name(&mut self) -> Self {
let mut subpool: *mut apr_sys::apr_pool_t = core::ptr::null_mut();
let location = core::concat!(file!(), ":", line!());
Pool::new_debug(location)
}
};
}
impl Default for Pool<'static> {
fn default() -> Self {
Self::new()
}
}
impl Pool<'static> {
pub fn new() -> Self {
let mut pool: *mut apr_sys::apr_pool_t = core::ptr::null_mut();
unsafe {
apr_sys::apr_pool_create_ex(
&mut pool,
core::ptr::null_mut(),
None,
core::ptr::null_mut(),
);
}
Pool {
raw: pool,
_marker: core::marker::PhantomData,
}
}
#[cfg(feature = "pool-debug")]
pub fn new_debug(location: &str) -> Self {
let mut pool: *mut apr_sys::apr_pool_t = core::ptr::null_mut();
unsafe {
apr_sys::apr_pool_create_ex_debug(
&mut pool,
core::ptr::null_mut(),
None,
core::ptr::null_mut(),
location.as_ptr() as *const core::ffi::c_char,
);
}
Pool {
raw: pool,
_marker: core::marker::PhantomData,
}
}
}
impl<'pool> Pool<'pool> {
pub unsafe fn from_raw(ptr: *mut apr_sys::apr_pool_t) -> Self {
Pool {
raw: ptr,
_marker: core::marker::PhantomData,
}
}
pub fn as_ptr(&self) -> *const apr_sys::apr_pool_t {
self.raw
}
pub fn as_mut_ptr(&self) -> *mut apr_sys::apr_pool_t {
self.raw
}
pub fn subpool(&self) -> Pool<'_> {
let mut subpool: *mut apr_sys::apr_pool_t = core::ptr::null_mut();
unsafe {
apr_sys::apr_pool_create_ex(&mut subpool, self.raw, None, core::ptr::null_mut());
}
Pool {
raw: subpool,
_marker: core::marker::PhantomData,
}
}
pub fn with_child<R>(&self, f: impl FnOnce(&Pool<'_>) -> R) -> R {
let child = self.subpool();
f(&child)
}
#[allow(clippy::mut_from_ref)]
pub fn alloc<T: Sized>(&self) -> *mut core::mem::MaybeUninit<T> {
let size = core::mem::size_of::<T>();
unsafe { apr_sys::apr_palloc(self.raw, size) as *mut core::mem::MaybeUninit<T> }
}
#[allow(clippy::mut_from_ref)]
pub fn calloc<T: Sized>(&self) -> *mut T {
let size = core::mem::size_of::<T>();
unsafe {
let x = apr_sys::apr_palloc(self.raw, size) as *mut T;
core::ptr::write_bytes(x as *mut u8, 0, size);
x
}
}
pub fn is_ancestor(&self, other: &Pool<'_>) -> bool {
unsafe { apr_sys::apr_pool_is_ancestor(self.raw, other.raw) != 0 }
}
pub fn tag(&self, tag: &str) {
use alloc::ffi::CString;
let tag = CString::new(tag).unwrap();
unsafe {
apr_sys::apr_pool_tag(self.raw, tag.as_ptr() as *const core::ffi::c_char);
}
}
pub fn pstrdup(&self, s: &str) -> *const core::ffi::c_char {
use alloc::ffi::CString;
let c_str = CString::new(s).expect("Invalid C string");
unsafe { apr_sys::apr_pstrdup(self.raw, c_str.as_ptr()) }
}
pub unsafe fn clear(&mut self) {
unsafe {
apr_sys::apr_pool_clear(self.raw);
}
}
#[cfg(feature = "pool-debug")]
pub unsafe fn fn_clear_debug(&mut self, location: &str) {
unsafe {
apr_sys::apr_pool_clear_debug(self.raw, location.as_ptr() as *const core::ffi::c_char);
}
}
pub fn parent<'a, 'b>(&'a self) -> Option<&'b Self>
where
'b: 'a,
{
let parent = unsafe { apr_sys::apr_pool_parent_get(self.raw) };
if parent.is_null() {
None
} else {
Some(unsafe { &*(parent as *const Pool) })
}
}
pub fn cleanup_for_exec(&self) {
unsafe {
apr_sys::apr_pool_cleanup_for_exec();
}
}
#[cfg(feature = "pool-debug")]
pub fn join(&self, other: &Pool<'_>) {
unsafe { apr_sys::apr_pool_join(self.raw, other.raw) }
}
#[cfg(feature = "pool-debug")]
pub fn num_bytes(&self, recurse: bool) -> usize {
unsafe { apr_sys::apr_pool_num_bytes(self.raw, if recurse { 1 } else { 0 }) }
}
#[cfg(feature = "pool-debug")]
pub unsafe fn find(&self, ptr: *const core::ffi::c_void) -> Option<Pool> {
let pool = apr_sys::apr_pool_find(ptr);
if pool.is_null() {
None
} else {
Some(Pool {
raw: pool,
_marker: core::marker::PhantomData,
})
}
}
#[cfg(not(feature = "pool-debug"))]
pub fn join(&self, _other: &Pool<'_>) {}
}
impl Drop for Pool<'_> {
fn drop(&mut self) {
unsafe {
apr_sys::apr_pool_destroy(self.raw);
}
}
}
pub struct Allocator {
raw: *mut apr_sys::apr_allocator_t,
_no_send: core::marker::PhantomData<*mut ()>,
}
impl Allocator {
pub fn new() -> Self {
let mut allocator: *mut apr_sys::apr_allocator_t = core::ptr::null_mut();
unsafe {
apr_sys::apr_allocator_create(&mut allocator);
}
Allocator {
raw: allocator,
_no_send: core::marker::PhantomData,
}
}
pub fn as_ptr(&self) -> *const apr_sys::apr_allocator_t {
self.raw
}
}
impl Default for Allocator {
fn default() -> Self {
Allocator::new()
}
}
impl Drop for Allocator {
fn drop(&mut self) {
unsafe {
apr_sys::apr_allocator_destroy(self.raw);
}
}
}
pub fn with_tmp_pool<R>(f: impl FnOnce(&Pool<'_>) -> R) -> R {
let tmp_pool = Pool::new();
f(&tmp_pool)
}
#[derive(Debug)]
pub enum PoolHandle<'pool> {
Owned(Pool<'pool>),
Borrowed(ManuallyDrop<Pool<'pool>>),
}
impl<'pool> PoolHandle<'pool> {
pub fn owned(pool: Pool<'pool>) -> Self {
PoolHandle::Owned(pool)
}
pub unsafe fn from_borrowed_raw(ptr: *mut apr_sys::apr_pool_t) -> Self {
PoolHandle::Borrowed(ManuallyDrop::new(Pool::from_raw(ptr)))
}
pub fn as_ptr(&self) -> *const apr_sys::apr_pool_t {
match self {
PoolHandle::Owned(pool) => pool.as_ptr(),
PoolHandle::Borrowed(pool) => pool.as_ptr(),
}
}
pub fn as_mut_ptr(&self) -> *mut apr_sys::apr_pool_t {
match self {
PoolHandle::Owned(pool) => pool.as_mut_ptr(),
PoolHandle::Borrowed(pool) => pool.as_mut_ptr(),
}
}
pub fn subpool(&self) -> Pool<'static> {
let mut subpool: *mut apr_sys::apr_pool_t = core::ptr::null_mut();
unsafe {
apr_sys::apr_pool_create_ex(
&mut subpool,
self.as_mut_ptr(),
None,
core::ptr::null_mut(),
);
Pool::from_raw(subpool)
}
}
pub fn is_owned(&self) -> bool {
matches!(self, PoolHandle::Owned(_))
}
pub fn is_borrowed(&self) -> bool {
matches!(self, PoolHandle::Borrowed(_))
}
}
impl Drop for PoolHandle<'_> {
fn drop(&mut self) {
}
}
impl<'pool> From<Pool<'pool>> for PoolHandle<'pool> {
fn from(pool: Pool<'pool>) -> Self {
PoolHandle::Owned(pool)
}
}
impl<'pool> Deref for PoolHandle<'pool> {
type Target = Pool<'pool>;
fn deref(&self) -> &Pool<'pool> {
match self {
PoolHandle::Owned(pool) => pool,
PoolHandle::Borrowed(pool) => pool,
}
}
}
impl<'pool> DerefMut for PoolHandle<'pool> {
fn deref_mut(&mut self) -> &mut Pool<'pool> {
match self {
PoolHandle::Owned(pool) => pool,
PoolHandle::Borrowed(pool) => pool,
}
}
}
#[derive(Debug, Clone)]
pub struct SharedPool<'pool> {
inner: Rc<Pool<'pool>>,
}
impl<'pool> SharedPool<'pool> {
pub fn new() -> SharedPool<'static> {
SharedPool {
inner: Rc::new(Pool::new()),
}
}
pub fn from_pool(pool: Pool<'pool>) -> Self {
SharedPool {
inner: Rc::new(pool),
}
}
pub fn strong_count(&self) -> usize {
Rc::strong_count(&self.inner)
}
pub fn as_ptr(&self) -> *const apr_sys::apr_pool_t {
self.inner.as_ptr()
}
pub fn as_mut_ptr(&self) -> *mut apr_sys::apr_pool_t {
self.inner.as_mut_ptr()
}
}
impl Default for SharedPool<'static> {
fn default() -> Self {
Self::new()
}
}
impl<'pool> Deref for SharedPool<'pool> {
type Target = Pool<'pool>;
fn deref(&self) -> &Pool<'pool> {
&self.inner
}
}
impl<'pool> From<Pool<'pool>> for SharedPool<'pool> {
fn from(pool: Pool<'pool>) -> Self {
SharedPool::from_pool(pool)
}
}
pub unsafe fn terminate() {
unsafe {
apr_sys::apr_pool_terminate();
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn test_pool() {
let pool = Pool::new();
assert!(pool.parent().unwrap().is_ancestor(&pool));
let parent = pool.parent();
assert!(parent.unwrap().is_ancestor(&pool));
let subpool = pool.subpool();
assert!(pool.is_ancestor(&subpool));
assert!(!subpool.is_ancestor(&pool));
assert!(subpool.parent().unwrap().is_ancestor(&subpool));
subpool.tag("subpool");
pool.tag("pool");
}
#[test]
fn test_pool_handle_owned() {
let pool = Pool::new();
let handle = PoolHandle::owned(pool);
assert!(handle.is_owned());
assert!(!handle.is_borrowed());
assert!(!handle.as_ptr().is_null());
assert!(!handle.as_mut_ptr().is_null());
handle.tag("owned-pool");
}
#[test]
fn test_pool_handle_borrowed() {
let pool = Pool::new();
let pool_ptr = pool.as_mut_ptr();
let handle = unsafe { PoolHandle::from_borrowed_raw(pool_ptr) };
assert!(handle.is_borrowed());
assert!(!handle.is_owned());
assert_eq!(handle.as_ptr(), pool_ptr);
assert_eq!(handle.as_mut_ptr(), pool_ptr);
handle.tag("borrowed-pool");
drop(handle);
pool.tag("still-valid");
}
#[test]
fn test_pool_handle_subpool() {
let pool = Pool::new();
let handle = PoolHandle::owned(pool);
let subpool = handle.subpool();
subpool.tag("subpool");
let pool2 = Pool::new();
let pool2_ptr = pool2.as_mut_ptr();
let borrowed_handle = unsafe { PoolHandle::from_borrowed_raw(pool2_ptr) };
let subpool2 = borrowed_handle.subpool();
subpool2.tag("subpool2");
}
#[test]
fn test_pool_handle_from_pool() {
let pool = Pool::new();
let handle: PoolHandle = pool.into();
assert!(handle.is_owned());
assert!(!handle.is_borrowed());
}
#[test]
fn test_pool_handle_deref() {
let pool = Pool::new();
let handle = PoolHandle::owned(pool);
handle.tag("test-deref");
let _subpool = handle.subpool();
let _parent = handle.parent();
let pool2 = Pool::new();
let pool2_ptr = pool2.as_mut_ptr();
let borrowed_handle = unsafe { PoolHandle::from_borrowed_raw(pool2_ptr) };
borrowed_handle.tag("test-deref-borrowed");
let _subpool2 = borrowed_handle.subpool();
}
#[test]
fn test_pool_handle_no_double_free() {
let pool = Pool::new();
let pool_ptr = pool.as_mut_ptr();
for i in 0..10 {
let handle = unsafe { PoolHandle::from_borrowed_raw(pool_ptr) };
handle.tag(&format!("iteration-{}", i));
}
pool.tag("still-valid-after-multiple-borrows");
}
#[test]
fn test_pool_handle_mixed_ownership() {
let long_lived_pool = Pool::new();
let long_lived_ptr = long_lived_pool.as_mut_ptr();
let result_handle = unsafe { PoolHandle::from_borrowed_raw(long_lived_ptr) };
let scratch_pool = Pool::new();
let scratch_handle = PoolHandle::owned(scratch_pool);
result_handle.tag("result-pool");
scratch_handle.tag("scratch-pool");
drop(scratch_handle);
drop(result_handle);
long_lived_pool.tag("still-valid");
}
#[test]
fn test_shared_pool_basic() {
let pool = SharedPool::new();
assert_eq!(pool.strong_count(), 1);
assert!(!pool.as_ptr().is_null());
assert!(!pool.as_mut_ptr().is_null());
pool.tag("shared-pool");
}
#[test]
fn test_shared_pool_clone() {
let pool1 = SharedPool::new();
assert_eq!(pool1.strong_count(), 1);
let pool2 = pool1.clone();
assert_eq!(pool1.strong_count(), 2);
assert_eq!(pool2.strong_count(), 2);
assert_eq!(pool1.as_ptr(), pool2.as_ptr());
pool1.tag("shared-pool-1");
pool2.tag("shared-pool-2");
let pool3 = pool2.clone();
assert_eq!(pool1.strong_count(), 3);
assert_eq!(pool2.strong_count(), 3);
assert_eq!(pool3.strong_count(), 3);
drop(pool3);
assert_eq!(pool1.strong_count(), 2);
assert_eq!(pool2.strong_count(), 2);
}
#[test]
fn test_shared_pool_from_pool() {
let pool = Pool::new();
pool.tag("original");
let shared = SharedPool::from_pool(pool);
assert_eq!(shared.strong_count(), 1);
}
#[test]
fn test_shared_pool_from_conversion() {
let pool = Pool::new();
let shared: SharedPool = pool.into();
assert_eq!(shared.strong_count(), 1);
shared.tag("converted");
}
#[test]
fn test_shared_pool_default() {
let pool = SharedPool::default();
assert_eq!(pool.strong_count(), 1);
pool.tag("default-pool");
}
#[test]
fn test_shared_pool_subpool() {
let shared = SharedPool::new();
shared.tag("shared-parent");
let subpool = shared.subpool();
subpool.tag("subpool");
assert!(shared.is_ancestor(&subpool));
assert!(!subpool.is_ancestor(&*shared));
}
#[test]
fn test_shared_pool_multiple_owners() {
let pool = SharedPool::new();
pool.tag("shared");
let component1 = pool.clone();
let component2 = pool.clone();
let component3 = pool.clone();
assert_eq!(pool.strong_count(), 4);
component1.pstrdup("component1");
component2.pstrdup("component2");
component3.pstrdup("component3");
drop(component1);
assert_eq!(pool.strong_count(), 3);
drop(component2);
assert_eq!(pool.strong_count(), 2);
drop(component3);
assert_eq!(pool.strong_count(), 1);
pool.pstrdup("still-valid");
}
}