Skip to main content

COMMON_CODE

Constant COMMON_CODE 

Source
pub const COMMON_CODE: &str = "// \u{2500}\u{2500}\u{2500} Compilation contexts (read before adding code here) \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\n// This file compiles in TWO contexts:\n//   1. as `mod common` inside rusteron-code-gen itself (unit tests, shared types), and\n//   2. verbatim inside every generated crate\'s `aeron.rs` (via COMMON_CODE include_str).\n// Consequence: nothing here may depend on impls that live in `aeron_custom*.rs` \u{2014} those\n// exist only in context 2 (e.g. AeronCError\'s Debug/Display). That is why AeronOfferError\n// hand-rolls its Debug. Crate-specific code belongs in `aeron_custom.rs` (all crates) or\n// `aeron_custom_<crate>.rs` (one crate); build-script-only code goes in `build_common.rs`.\n// \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\n\nuse crate::AeronErrorType::Unknown;\n#[cfg(feature = \"backtrace\")]\nuse std::backtrace::Backtrace;\nuse std::cell::UnsafeCell;\nuse std::fmt::Formatter;\nuse std::mem::MaybeUninit;\nuse std::ops::Deref;\n#[allow(unused_imports)]\nuse std::ops::DerefMut;\n\n/// Reference-counting smart pointer: `Rc` by default, `Arc` under the\n/// `multi-threaded` feature. Swap is transparent \u{2014} `RcOrArc::new`, `.clone()`,\n/// `strong_count` all work on both.\n#[cfg(not(feature = \"multi-threaded\"))]\npub type RcOrArc<T> = std::rc::Rc<T>;\n#[cfg(feature = \"multi-threaded\")]\npub type RcOrArc<T> = std::sync::Arc<T>;\n\n#[cfg(not(feature = \"multi-threaded\"))]\npub type RefCellOrMutex<T> = std::cell::RefCell<T>;\n#[cfg(feature = \"multi-threaded\")]\npub type RefCellOrMutex<T> = std::sync::Mutex<T>;\n\n#[cfg(not(feature = \"multi-threaded\"))]\npub type CleanupBox<T> = Box<dyn FnMut(*mut *mut T) -> i32>;\n#[cfg(feature = \"multi-threaded\")]\npub type CleanupBox<T> = Box<dyn FnMut(*mut *mut T) -> i32 + Send>;\n\npub enum CResource<T> {\n    OwnedOnHeap(RcOrArc<ManagedCResource<T>>),\n    /// Always initialised by construction (zeroed or `new(v)`). Never store\n    /// `uninit()` \u{2014} `Clone` and `get()` assume it\'s valid.\n    OwnedOnStack(std::mem::MaybeUninit<T>),\n    Borrowed(*mut T),\n}\n\nimpl<T: Clone> Clone for CResource<T> {\n    fn clone(&self) -> Self {\n        // SAFETY: each branch only dereferences pointers/references that are\n        // valid by construction. `OwnedOnStack` upholds the initialised-by-\n        // construction invariant documented on the variant, so `assume_init_ref`\n        // is sound.\n        unsafe {\n            match self {\n                CResource::OwnedOnHeap(r) => CResource::OwnedOnHeap(r.clone()),\n                CResource::OwnedOnStack(r) => CResource::OwnedOnStack(MaybeUninit::new(r.assume_init_ref().clone())),\n                CResource::Borrowed(r) => CResource::Borrowed(r.clone()),\n            }\n        }\n    }\n}\n\nimpl<T> CResource<T> {\n    #[inline]\n    pub fn get(&self) -> *mut T {\n        match self {\n            CResource::OwnedOnHeap(r) => r.get(),\n            CResource::OwnedOnStack(r) => r.as_ptr() as *mut T,\n            CResource::Borrowed(r) => *r,\n        }\n    }\n\n    #[inline]\n    // to prevent the dependencies from being dropped as you have a copy here\n    pub fn add_dependency<D: std::any::Any>(&self, dep: D) {\n        match self {\n            CResource::OwnedOnHeap(r) => r.add_dependency(dep),\n            CResource::OwnedOnStack(_) | CResource::Borrowed(_) => {\n                unreachable!(\"only owned on heap\")\n            }\n        }\n    }\n    #[inline]\n    pub fn get_dependency<V: Clone + \'static>(&self) -> Option<V> {\n        match self {\n            CResource::OwnedOnHeap(r) => r.get_dependency(),\n            CResource::OwnedOnStack(_) | CResource::Borrowed(_) => None,\n        }\n    }\n\n    #[inline]\n    pub fn as_owned(&self) -> Option<&RcOrArc<ManagedCResource<T>>> {\n        match self {\n            CResource::OwnedOnHeap(r) => Some(r),\n            CResource::OwnedOnStack(_) | CResource::Borrowed(_) => None,\n        }\n    }\n\n    /// Run the clean-up / close on the resource via its shared state.\n    ///\n    /// For `OwnedOnHeap` resources this calls `close_shared` on the\n    /// `ManagedCResource` \u{2014} the FFI close fires exactly once across all\n    /// clones.  Stack and borrowed resources are no-ops (they don\'t own a\n    /// cleanup closure).\n    #[allow(dead_code)]\n    #[inline]\n    pub(crate) fn close_resource(&self) -> Result<(), AeronCError> {\n        match self {\n            CResource::OwnedOnHeap(r) => r.close_shared(),\n            CResource::OwnedOnStack(_) | CResource::Borrowed(_) => Ok(()),\n        }\n    }\n\n    /// Run a custom close function through the same shared close gate.\n    ///\n    /// This is used for close methods that take extra parameters, such as\n    /// Aeron\'s close-complete notification callback.  The custom close still\n    /// consumes the wrapper handle and still closes exactly once across clones.\n    #[allow(dead_code)]\n    #[inline]\n    pub(crate) fn close_resource_with(&self, cleanup: impl FnMut(*mut *mut T) -> i32) -> Result<(), AeronCError> {\n        match self {\n            CResource::OwnedOnHeap(r) => r.close_shared_with(cleanup),\n            CResource::OwnedOnStack(_) | CResource::Borrowed(_) => Ok(()),\n        }\n    }\n\n    /// Close an owner resource only when this is the last shared reference.\n    ///\n    /// This is for owner/client handles (e.g. Aeron/AeronArchive) whose C close\n    /// frees child resources.  If child handles still hold dependency clones, we\n    /// must defer to natural Rc teardown to preserve child-before-parent order.\n    #[allow(dead_code)]\n    #[inline]\n    pub(crate) fn close_resource_deferred_if_shared(&self) -> Result<(), AeronCError> {\n        match self {\n            CResource::OwnedOnHeap(r) => {\n                let refs = RcOrArc::strong_count(r);\n                if refs > 1 {\n                    log::info!(\n                        \"close deferred for {} because {} references are still alive\",\n                        std::any::type_name::<T>(),\n                        refs\n                    );\n                    Ok(())\n                } else {\n                    r.close_shared()\n                }\n            }\n            CResource::OwnedOnStack(_) | CResource::Borrowed(_) => Ok(()),\n        }\n    }\n}\n\nimpl<T> std::fmt::Debug for CResource<T> {\n    fn fmt(&self, f: &mut Formatter<\'_>) -> std::fmt::Result {\n        let name = std::any::type_name::<T>();\n\n        match self {\n            CResource::OwnedOnHeap(r) => {\n                write!(f, \"{name} heap({:?})\", r)\n            }\n            CResource::OwnedOnStack(r) => {\n                write!(f, \"{name} stack({:?})\", *r)\n            }\n            CResource::Borrowed(r) => {\n                write!(f, \"{name} borrowed ({:?})\", r)\n            }\n        }\n    }\n}\n\n/// A custom struct for managing C resources with automatic cleanup.\n///\n/// It handles initialisation and clean-up of the resource and ensures that resources\n/// are properly released when they go out of scope. All teardown goes through the\n/// single `cleanup` closure (if set), which is the FFI close function (e.g.\n/// `aeron_close`). The Rc dependency graph ensures parents outlive children\n/// structurally \u{2014} you cannot race `aeron_close` ahead of a live child handle.\n#[allow(dead_code)]\n#[allow(dead_code)]\npub struct ManagedCResource<T> {\n    #[cfg(not(feature = \"multi-threaded\"))]\n    resource: std::cell::Cell<*mut T>,\n    #[cfg(feature = \"multi-threaded\")]\n    resource: std::sync::atomic::AtomicPtr<T>,\n\n    #[cfg(not(feature = \"multi-threaded\"))]\n    cleanup: UnsafeCell<Option<CleanupBox<T>>>,\n    #[cfg(feature = \"multi-threaded\")]\n    cleanup: std::sync::Mutex<Option<CleanupBox<T>>>,\n\n    cleanup_struct: bool,\n\n    manual_close_required: bool,\n\n    #[cfg(not(feature = \"multi-threaded\"))]\n    close_already_called: std::cell::Cell<bool>,\n    #[cfg(feature = \"multi-threaded\")]\n    close_already_called: std::sync::atomic::AtomicBool,\n\n    #[cfg(not(feature = \"multi-threaded\"))]\n    resource_released: std::cell::Cell<bool>,\n    #[cfg(feature = \"multi-threaded\")]\n    resource_released: std::sync::atomic::AtomicBool,\n\n    #[cfg(not(feature = \"multi-threaded\"))]\n    dependencies: UnsafeCell<Vec<RcOrArc<dyn std::any::Any>>>,\n    #[cfg(feature = \"multi-threaded\")]\n    dependencies: std::sync::Mutex<Vec<RcOrArc<dyn std::any::Any>>>,\n}\n\nimpl<T> std::fmt::Debug for ManagedCResource<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<\'_>) -> std::fmt::Result {\n        let mut debug = f.debug_struct(\"ManagedCResource\");\n        if self.get_close_already_called() {\n            debug.field(\"resource\", &\"<closed>\");\n        } else {\n            debug.field(\"resource\", &self.get());\n        }\n        debug.field(\"type\", &std::any::type_name::<T>()).finish()\n    }\n}\n\n// Under `multi-threaded` the refcount is `Arc` (atomic), so `Send` is sound.\n// `Sync` enables sharing `&Handle` across threads for the C-documented\n// thread-safe operations (offer / try_claim / position). The `UnsafeCell`\n// fields are only mutated during construction and close (single-threaded),\n// never during the shared-read window \u{2014} same \"accepted unsoundness\" policy\n// as the unconditional `unsafe impl Send` on the handle types.\n#[cfg(feature = \"multi-threaded\")]\nunsafe impl<T> Send for ManagedCResource<T> {}\n#[cfg(feature = \"multi-threaded\")]\nunsafe impl<T> Sync for ManagedCResource<T> {}\n\nimpl<T> ManagedCResource<T> {\n    /// Creates a new ManagedCResource with a given initializer and cleanup function.\n    ///\n    /// The initializer is a closure that attempts to initialize the resource.\n    /// If initialization fails, the initializer should return an error code.\n    /// The cleanup function is used to release the resource when it\'s no longer needed.\n    /// `cleanup_struct` where it should clean up the struct in rust\n    pub fn new(\n        init: impl FnOnce(*mut *mut T) -> i32,\n        cleanup: Option<CleanupBox<T>>,\n        cleanup_struct: bool,\n    ) -> Result<Self, AeronCError> {\n        let resource = Self::initialise(init)?;\n        // Compute before `cleanup` is moved into the struct literal below.\n        // `is_none()` borrows `cleanup` immutably; the move happens later.\n        let manual_close_required = cleanup.is_none() && !cleanup_struct;\n\n        let result = Self {\n            #[cfg(not(feature = \"multi-threaded\"))]\n            resource: std::cell::Cell::new(resource),\n            #[cfg(feature = \"multi-threaded\")]\n            resource: std::sync::atomic::AtomicPtr::new(resource),\n\n            #[cfg(not(feature = \"multi-threaded\"))]\n            cleanup: UnsafeCell::new(cleanup),\n            #[cfg(feature = \"multi-threaded\")]\n            cleanup: std::sync::Mutex::new(cleanup),\n\n            cleanup_struct,\n            manual_close_required,\n\n            #[cfg(not(feature = \"multi-threaded\"))]\n            close_already_called: std::cell::Cell::new(false),\n            #[cfg(feature = \"multi-threaded\")]\n            close_already_called: std::sync::atomic::AtomicBool::new(false),\n\n            #[cfg(not(feature = \"multi-threaded\"))]\n            resource_released: std::cell::Cell::new(false),\n            #[cfg(feature = \"multi-threaded\")]\n            resource_released: std::sync::atomic::AtomicBool::new(false),\n\n            #[cfg(not(feature = \"multi-threaded\"))]\n            dependencies: UnsafeCell::new(vec![]),\n            #[cfg(feature = \"multi-threaded\")]\n            dependencies: std::sync::Mutex::new(vec![]),\n        };\n        #[cfg(feature = \"extra-logging\")]\n        log::info!(\"created c resource: {:?}\", result);\n        Ok(result)\n    }\n\n    pub fn initialise(init: impl FnOnce(*mut *mut T) -> i32 + Sized) -> Result<*mut T, AeronCError> {\n        let mut resource: *mut T = std::ptr::null_mut();\n        let result = init(&mut resource);\n        if result < 0 || resource.is_null() {\n            return Err(AeronCError::from_code(result));\n        }\n        Ok(resource)\n    }\n\n    /// Gets a raw pointer to the resource.\n    #[inline(always)]\n    pub fn get(&self) -> *mut T {\n        #[cfg(not(feature = \"multi-threaded\"))]\n        {\n            self.resource.get()\n        }\n        #[cfg(feature = \"multi-threaded\")]\n        {\n            self.resource.load(std::sync::atomic::Ordering::Acquire)\n        }\n    }\n\n    #[inline(always)]\n    fn set_resource(&self, val: *mut T) {\n        #[cfg(not(feature = \"multi-threaded\"))]\n        {\n            self.resource.set(val);\n        }\n        #[cfg(feature = \"multi-threaded\")]\n        {\n            self.resource.store(val, std::sync::atomic::Ordering::Release);\n        }\n    }\n\n    #[inline(always)]\n    fn get_close_already_called(&self) -> bool {\n        #[cfg(not(feature = \"multi-threaded\"))]\n        {\n            self.close_already_called.get()\n        }\n        #[cfg(feature = \"multi-threaded\")]\n        {\n            self.close_already_called.load(std::sync::atomic::Ordering::Acquire)\n        }\n    }\n\n    #[inline(always)]\n    fn set_close_already_called(&self, val: bool) {\n        #[cfg(not(feature = \"multi-threaded\"))]\n        {\n            self.close_already_called.set(val);\n        }\n        #[cfg(feature = \"multi-threaded\")]\n        {\n            self.close_already_called\n                .store(val, std::sync::atomic::Ordering::Release);\n        }\n    }\n\n    #[inline(always)]\n    fn get_resource_released(&self) -> bool {\n        #[cfg(not(feature = \"multi-threaded\"))]\n        {\n            self.resource_released.get()\n        }\n        #[cfg(feature = \"multi-threaded\")]\n        {\n            self.resource_released.load(std::sync::atomic::Ordering::Acquire)\n        }\n    }\n\n    #[inline(always)]\n    fn set_resource_released(&self, val: bool) {\n        #[cfg(not(feature = \"multi-threaded\"))]\n        {\n            self.resource_released.set(val);\n        }\n        #[cfg(feature = \"multi-threaded\")]\n        {\n            self.resource_released.store(val, std::sync::atomic::Ordering::Release);\n        }\n    }\n\n    /// Mutable access to the underlying C struct, minted from `&self`.\n    ///\n    /// # Safety\n    /// No other reference (`&` or `&mut`) to the underlying struct may be\n    /// alive while the returned `&mut` is in use.\n    #[inline(always)]\n    pub unsafe fn get_mut(&self) -> &mut T {\n        &mut *self.get()\n    }\n\n    #[inline]\n    // to prevent the dependencies from being dropped as you have a copy here\n    pub fn add_dependency<D: std::any::Any>(&self, dep: D) {\n        if let Some(dep) = (&dep as &dyn std::any::Any).downcast_ref::<RcOrArc<dyn std::any::Any>>() {\n            #[cfg(not(feature = \"multi-threaded\"))]\n            unsafe {\n                (*self.dependencies.get()).push(dep.clone());\n            }\n            #[cfg(feature = \"multi-threaded\")]\n            {\n                self.dependencies.lock().unwrap().push(dep.clone());\n            }\n        } else {\n            #[cfg(not(feature = \"multi-threaded\"))]\n            unsafe {\n                (*self.dependencies.get()).push(RcOrArc::new(dep));\n            }\n            #[cfg(feature = \"multi-threaded\")]\n            {\n                self.dependencies.lock().unwrap().push(RcOrArc::new(dep));\n            }\n        }\n    }\n\n    #[inline]\n    pub fn get_dependency<V: Clone + \'static>(&self) -> Option<V> {\n        #[cfg(not(feature = \"multi-threaded\"))]\n        unsafe {\n            (*self.dependencies.get())\n                .iter()\n                .filter_map(|x| x.as_ref().downcast_ref::<V>().cloned())\n                .next()\n        }\n        #[cfg(feature = \"multi-threaded\")]\n        {\n            self.dependencies\n                .lock()\n                .unwrap()\n                .iter()\n                .filter_map(|x| x.as_ref().downcast_ref::<V>().cloned())\n                .next()\n        }\n    }\n\n    #[inline]\n    pub fn is_resource_released(&self) -> bool {\n        self.get_resource_released()\n    }\n\n    #[inline]\n    pub fn mark_resource_released(&self) {\n        self.set_resource_released(true);\n        // The C client frees async resources when their poll completes (created,\n        // errored, or cancelled). Null the stale pointer so any later use faults\n        // deterministically on null instead of reading freed memory.\n        self.set_resource(std::ptr::null_mut());\n    }\n\n    /// Closes the resource through a shared reference.\n    ///\n    /// Like `close(&mut self)` but works with `&self`, enabling explicit close\n    /// on handles that share the resource via `Rc`.  The cleanup closure is\n    /// accessed through `UnsafeCell` interior mutability; the\n    /// `close_already_called` gate ensures it is only taken once regardless of\n    /// how many clones call `close_shared()`.\n    ///\n    /// This is the method called by the generated `close(self)` method on\n    /// wrapper types.\n    pub(crate) fn close_shared(&self) -> Result<(), AeronCError> {\n        if self.get_close_already_called() {\n            return Ok(());\n        }\n\n        // SAFETY: this library deliberately uses Rc/Cell/UnsafeCell for\n        // single-threaded low-latency handles. close_shared() is not Sync; the\n        // first caller takes the cleanup closure and either completes close or\n        // restores the closure on failure so a later call/drop can retry.\n        #[cfg(not(feature = \"multi-threaded\"))]\n        let cleanup = unsafe { (*self.cleanup.get()).take() };\n        #[cfg(feature = \"multi-threaded\")]\n        let cleanup = self.cleanup.lock().unwrap().take();\n\n        if let Some(mut cleanup) = cleanup {\n            let mut resource = self.get();\n            if !resource.is_null() {\n                let result = cleanup(&mut resource);\n                if result < 0 {\n                    #[cfg(not(feature = \"multi-threaded\"))]\n                    unsafe {\n                        *self.cleanup.get() = Some(cleanup);\n                    }\n                    #[cfg(feature = \"multi-threaded\")]\n                    {\n                        *self.cleanup.lock().unwrap() = Some(cleanup);\n                    }\n                    return Err(AeronCError::from_code(result));\n                }\n            }\n\n            self.set_close_already_called(true);\n            if !self.cleanup_struct {\n                // C-owned resources have been freed by the close function.\n                // Null the shared pointer so clones cannot keep using a\n                // dangling pointer after explicit close.\n                self.set_resource(std::ptr::null_mut());\n            }\n        } else {\n            self.set_close_already_called(true);\n        }\n\n        Ok(())\n    }\n\n    /// Closes the resource with a caller-supplied C close function.\n    ///\n    /// The stored default cleanup is taken first so Drop cannot later run it a\n    /// second time.  If the custom close fails, the default cleanup is restored\n    /// and the resource remains retryable.\n    #[allow(dead_code)]\n    pub(crate) fn close_shared_with(\n        &self,\n        mut custom_cleanup: impl FnMut(*mut *mut T) -> i32,\n    ) -> Result<(), AeronCError> {\n        if self.get_close_already_called() {\n            return Ok(());\n        }\n\n        #[cfg(not(feature = \"multi-threaded\"))]\n        let stored_cleanup = unsafe { (*self.cleanup.get()).take() };\n        #[cfg(feature = \"multi-threaded\")]\n        let stored_cleanup = self.cleanup.lock().unwrap().take();\n\n        let mut resource = self.get();\n        if !resource.is_null() {\n            let result = custom_cleanup(&mut resource);\n            if result < 0 {\n                #[cfg(not(feature = \"multi-threaded\"))]\n                unsafe {\n                    *self.cleanup.get() = stored_cleanup;\n                }\n                #[cfg(feature = \"multi-threaded\")]\n                {\n                    *self.cleanup.lock().unwrap() = stored_cleanup;\n                }\n                return Err(AeronCError::from_code(result));\n            }\n        }\n\n        self.set_close_already_called(true);\n        if !self.cleanup_struct {\n            self.set_resource(std::ptr::null_mut());\n        }\n\n        Ok(())\n    }\n}\n\nimpl<T> Drop for ManagedCResource<T> {\n    fn drop(&mut self) {\n        // Capture whether close ran BEFORE Drop \u{2014} close_shared() below would set\n        // the flag even when the cleanup closure is None, hiding the leak signal.\n        let close_ran_before_drop = self.get_close_already_called();\n        // Delegate to close_shared() which handles single-execution, error\n        // logging, and pointer-nulling.  close_already_called prevents\n        // double-execution if the resource was already closed through\n        // another clone.\n        if !close_ran_before_drop {\n            if let Err(e) = self.close_shared() {\n                log::warn!(\n                    \"cleanup failed for {} during Drop with code {}\",\n                    std::any::type_name::<T>(),\n                    e.code,\n                );\n            }\n        }\n\n        if self.manual_close_required && !close_ran_before_drop {\n            #[cfg(not(feature = \"multi-threaded\"))]\n            let has_dependency = !unsafe { (*self.dependencies.get()).is_empty() };\n            #[cfg(feature = \"multi-threaded\")]\n            let has_dependency = !self.dependencies.lock().unwrap().is_empty();\n            if !has_dependency {\n                let resource = self.get();\n                if !resource.is_null() {\n                    #[cfg(feature = \"strict-lifecycle\")]\n                    panic!(\n                        \"ManagedCResource<{}> dropped without explicit close and no cleanup closure \\\n                         \u{2014} resource leaked. Call close()/close_now() before drop, or supply a \\\n                         cleanup closure at construction.\",\n                        std::any::type_name::<T>()\n                    );\n                    #[cfg(not(feature = \"strict-lifecycle\"))]\n                    log::warn!(\n                        \"ManagedCResource<{}> dropped without explicit close and no cleanup closure \\\n                         \u{2014} resource likely leaked. Call close()/close_now() before drop, or supply a \\\n                         cleanup closure at construction.\",\n                        std::any::type_name::<T>()\n                    );\n                }\n            }\n        }\n\n        if self.cleanup_struct {\n            let resource = self.get();\n            if !resource.is_null() {\n                #[cfg(feature = \"extra-logging\")]\n                log::info!(\"closing rust struct resource: {:?}\", resource);\n                unsafe {\n                    let _ = Box::from_raw(resource);\n                }\n                self.set_resource(std::ptr::null_mut());\n            }\n        }\n    }\n}\n\n#[derive(Debug, PartialOrd, Eq, PartialEq, Clone)]\npub enum AeronErrorType {\n    GenericError,\n    ClientErrorDriverTimeout,\n    ClientErrorClientTimeout,\n    ClientErrorConductorServiceTimeout,\n    ClientErrorBufferFull,\n    PublicationBackPressured,\n    PublicationAdminAction,\n    PublicationClosed,\n    PublicationMaxPositionExceeded,\n    PublicationError,\n    TimedOut,\n    Unknown(i32),\n}\n\nimpl From<AeronErrorType> for AeronCError {\n    fn from(value: AeronErrorType) -> Self {\n        AeronCError::from_code(value.code())\n    }\n}\n\nimpl AeronErrorType {\n    pub fn code(&self) -> i32 {\n        match self {\n            AeronErrorType::GenericError => -1,\n            AeronErrorType::ClientErrorDriverTimeout => -1000,\n            AeronErrorType::ClientErrorClientTimeout => -1001,\n            AeronErrorType::ClientErrorConductorServiceTimeout => -1002,\n            AeronErrorType::ClientErrorBufferFull => -1003,\n            AeronErrorType::PublicationBackPressured => -2,\n            AeronErrorType::PublicationAdminAction => -3,\n            AeronErrorType::PublicationClosed => -4,\n            AeronErrorType::PublicationMaxPositionExceeded => -5,\n            AeronErrorType::PublicationError => -6,\n            AeronErrorType::TimedOut => -234324,\n            AeronErrorType::Unknown(code) => *code,\n        }\n    }\n\n    pub fn is_back_pressured(&self) -> bool {\n        self == &AeronErrorType::PublicationBackPressured\n    }\n\n    pub fn is_admin_action(&self) -> bool {\n        self == &AeronErrorType::PublicationAdminAction\n    }\n\n    pub fn is_back_pressured_or_admin_action(&self) -> bool {\n        self.is_back_pressured() || self.is_admin_action()\n    }\n\n    pub fn from_code(code: i32) -> Self {\n        match code {\n            -1 => AeronErrorType::GenericError,\n            -1000 => AeronErrorType::ClientErrorDriverTimeout,\n            -1001 => AeronErrorType::ClientErrorClientTimeout,\n            -1002 => AeronErrorType::ClientErrorConductorServiceTimeout,\n            -1003 => AeronErrorType::ClientErrorBufferFull,\n            -2 => AeronErrorType::PublicationBackPressured,\n            -3 => AeronErrorType::PublicationAdminAction,\n            -4 => AeronErrorType::PublicationClosed,\n            -5 => AeronErrorType::PublicationMaxPositionExceeded,\n            -6 => AeronErrorType::PublicationError,\n            -234324 => AeronErrorType::TimedOut,\n            _ => Unknown(code),\n        }\n    }\n\n    pub fn to_string(&self) -> &\'static str {\n        match self {\n            AeronErrorType::GenericError => \"Generic Error\",\n            AeronErrorType::ClientErrorDriverTimeout => \"Client Error Driver Timeout\",\n            AeronErrorType::ClientErrorClientTimeout => \"Client Error Client Timeout\",\n            AeronErrorType::ClientErrorConductorServiceTimeout => \"Client Error Conductor Service Timeout\",\n            AeronErrorType::ClientErrorBufferFull => \"Client Error Buffer Full\",\n            AeronErrorType::PublicationBackPressured => \"Publication Back Pressured\",\n            AeronErrorType::PublicationAdminAction => \"Publication Admin Action\",\n            AeronErrorType::PublicationClosed => \"Publication Closed\",\n            AeronErrorType::PublicationMaxPositionExceeded => \"Publication Max Position Exceeded\",\n            AeronErrorType::PublicationError => \"Publication Error\",\n            AeronErrorType::TimedOut => \"Timed Out\",\n            AeronErrorType::Unknown(_) => \"Unknown Error\",\n        }\n    }\n}\n\n/// Aeron C API error: code + optional message.\n///\n/// Construction is allocation-free (never reads `aeron_errmsg()`), so retry loops\n/// that discard the error stay cheap. Attach the message via `capture_errmsg()`\n/// (at the error site) or `with_message()`; `Display` / `get_last_err_message()`\n/// otherwise read the live `aeron_errmsg()` buffer.\n#[derive(Clone)]\npub struct AeronCError {\n    pub code: i32,\n    /// Attached via `capture_errmsg()` / `with_message()`; `None` otherwise.\n    msg: Option<String>,\n}\n\n/// Equality is on `code` only \u{2014} the attached message is advisory.\nimpl PartialEq for AeronCError {\n    fn eq(&self, other: &Self) -> bool {\n        self.code == other.code\n    }\n}\nimpl Eq for AeronCError {}\n\nimpl AeronCError {\n    /// Construct from an Aeron error code (`< 0` is failure).\n    ///\n    /// Allocation-free; does not read `aeron_errmsg()`. Use `capture_errmsg()` to\n    /// attach the message when the error will be stored or logged later.\n    pub fn from_code(code: i32) -> Self {\n        #[cfg(feature = \"backtrace\")]\n        {\n            if code < 0 {\n                let backtrace = Backtrace::capture();\n                let backtrace = format!(\"{:?}\", backtrace);\n\n                // Compile the backtrace-parsing regex ONCE, not per error.\n                // `from_code` sits on the error path; re-compiling the regex on\n                // every Aeron error (including back-pressure retries) is wasteful.\n                static BACKTRACE_RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();\n                let re = BACKTRACE_RE\n                    .get_or_init(|| regex::Regex::new(r#\"fn: \"([^\"]+)\", file: \"([^\"]+)\", line: (\\d+)\"#).unwrap());\n                let mut lines = String::new();\n                re.captures_iter(&backtrace).for_each(|cap| {\n                    let function = &cap[1];\n                    let mut file = cap[2].to_string();\n                    let line = &cap[3];\n                    if file.starts_with(\"./\") {\n                        file = format!(\"{}/{}\", env!(\"CARGO_MANIFEST_DIR\"), &file[2..]);\n                    } else if file.starts_with(\"/rustc/\") {\n                        file = file.split(\"/\").last().unwrap().to_string();\n                    }\n                    // log in intellij friendly error format so can hyperlink to source code in stack trace\n                    lines.push_str(&format!(\" {file}:{line} in {function}\\n\"));\n                });\n\n                log::error!(\n                    \"Aeron C error code: {}, kind: \'{:?}\'\\n{}\",\n                    code,\n                    AeronErrorType::from_code(code),\n                    lines\n                );\n            }\n        }\n        AeronCError { code, msg: None }\n    }\n\n    /// [`Self::from_code`] with an attached message.\n    pub fn with_message(code: i32, msg: impl Into<String>) -> Self {\n        let mut err = Self::from_code(code);\n        err.msg = Some(msg.into());\n        err\n    }\n\n    /// Message attached via `capture_errmsg()` / `with_message()`, if any.\n    pub fn message(&self) -> Option<&str> {\n        self.msg.as_deref()\n    }\n\n    pub fn kind(&self) -> AeronErrorType {\n        AeronErrorType::from_code(self.code)\n    }\n\n    pub fn is_back_pressured(&self) -> bool {\n        self.kind().is_back_pressured()\n    }\n\n    pub fn is_admin_action(&self) -> bool {\n        self.kind().is_admin_action()\n    }\n\n    pub fn is_back_pressured_or_admin_action(&self) -> bool {\n        self.kind().is_back_pressured_or_admin_action()\n    }\n}\n\n/// Typed error for `offer` / `try_claim` on a publication.\n///\n/// Aeron returns a negative *sentinel* instead of a stream position. These are not\n/// errno-style codes \u{2014} `-1` here means \"not connected\", not a generic error \u{2014} hence\n/// this dedicated type. Use [`Self::is_retryable`] to drive retry loops.\n#[derive(Clone, PartialEq, Eq)]\npub enum AeronOfferError {\n    /// No subscriber is connected (`-1`). Usually transient: a subscriber may\n    /// connect later. Retryable.\n    NotConnected,\n    /// Flow control or a full term buffer is applying back pressure (`-2`).\n    /// Retry after idling. Retryable.\n    BackPressured,\n    /// An administrative action (e.g. term rotation) is in progress (`-3`).\n    /// Retry immediately. Retryable.\n    AdminAction,\n    /// The publication is closed (`-4`). Fatal for this handle.\n    Closed,\n    /// The maximum stream position was reached (`-5`). Fatal: a new publication\n    /// (new session) is required.\n    MaxPositionExceeded,\n    /// More than [`MAX_OFFER_PARTS`] buffers passed to `offer_parts` \u{2014} a caller\n    /// bug, not an Aeron wire error. Fatal (fix the call site).\n    TooManyParts,\n    /// Any other negative value (`-6` / unexpected). Fatal; inspect the inner\n    /// [`AeronCError`] and `Aeron::errmsg()` for detail.\n    Error(AeronCError),\n}\nimpl AeronOfferError {\n    /// Maps a raw offer/try_claim return to `Ok(position)` or a typed error.\n    #[inline]\n    pub fn from_position(position: i64) -> Result<i64, Self> {\n        if position >= 0 {\n            return Ok(position);\n        }\n        Err(match position {\n            -1 => AeronOfferError::NotConnected,\n            -2 => AeronOfferError::BackPressured,\n            -3 => AeronOfferError::AdminAction,\n            -4 => AeronOfferError::Closed,\n            -5 => AeronOfferError::MaxPositionExceeded,\n            _ => AeronOfferError::Error(AeronCError::from_code(position as i32)),\n        })\n    }\n\n    /// A retry (possibly after idling / waiting for a subscriber) can succeed.\n    #[inline]\n    pub fn is_retryable(&self) -> bool {\n        matches!(\n            self,\n            AeronOfferError::NotConnected | AeronOfferError::BackPressured | AeronOfferError::AdminAction\n        )\n    }\n\n    /// The publication will never accept this offer again; recreate or give up.\n    #[inline]\n    pub fn is_fatal(&self) -> bool {\n        !self.is_retryable()\n    }\n}\n\nimpl std::fmt::Display for AeronOfferError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<\'_>) -> std::fmt::Result {\n        match self {\n            AeronOfferError::NotConnected => write!(f, \"publication not connected\"),\n            AeronOfferError::BackPressured => write!(f, \"publication back pressured\"),\n            AeronOfferError::AdminAction => write!(f, \"publication admin action in progress\"),\n            AeronOfferError::Closed => write!(f, \"publication closed\"),\n            AeronOfferError::MaxPositionExceeded => write!(f, \"publication max position exceeded\"),\n            AeronOfferError::TooManyParts => write!(f, \"too many parts in offer_parts (max 8)\"),\n            AeronOfferError::Error(e) => write!(f, \"publication error (code {})\", e.code),\n        }\n    }\n}\n\nimpl std::fmt::Debug for AeronOfferError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<\'_>) -> std::fmt::Result {\n        std::fmt::Display::fmt(self, f)\n    }\n}\n\nimpl std::error::Error for AeronOfferError {}\n\n/// # Handler\n/// **Heap-allocated, reference-counted** callback holder for callbacks the C\n/// client **retains** (fires later, possibly many times, from the conductor thread).\n///\n/// `Handler<T>` wraps `Arc<UnsafeCell<T>>`. The callback value lives on the heap\n/// and is freed only when the last clone drops. The raw `clientd` pointer handed\n/// to C is `&T` (via [`Handler::as_raw`]); C keeps firing it for as long as it\n/// holds the callback, so the `Handler` must outlive that \u{2014} methods that register\n/// a retained callback ([`AeronContext::set_error_handler`], the image lifecycle\n/// handlers on `async_add_subscription`, `set_on_available_image`, \u{2026}) store a\n/// clone of the `Handler` inside the registering resource (as a dependency), so\n/// the value is guaranteed to outlive the C side\'s use of it. No manual\n/// `release()` is needed.\n///\n/// # Heap vs stack \u{2014} when to reach for `Handler` vs a `*_fn` / `*_once` method\n///\n/// | Callback kind | Where the closure lives | API |\n/// |---|---|---|\n/// | **Retained** (C stores it; fires later / repeatedly) | **heap** (`Handler`/`Arc`) | `set_error_handler(Some(Handler::new(...)))`, `async_add_subscription(.., Some(&h), ..)`, `poll(Some(&h), limit)` |\n/// | **Sync / call-only** (C fires it during the call, then is done) | **stack** (borrowed `FnMut`, zero allocation) | `poll_fn(\\|msg, hdr\\| ..., limit)`, the generated `*_once` variants |\n///\n/// Prefer the stack form (`poll_fn`, `*_once`) on the hot path: it borrows the\n/// closure for the duration of the call only, so there is no `Arc`, no heap\n/// allocation, and the closure may borrow local state. Reach for `Handler`\n/// (heap) when the callback must survive past the registering call \u{2014} image\n/// lifecycle handlers, error handlers, counters callbacks, anything the\n/// conductor invokes asynchronously.\n///\n/// The reference count is atomic (`Arc`), so a `Handler` may be moved to another\n/// thread; it is deliberately **not `Sync`** \u{2014} callbacks fire from the conductor\n/// thread and must not be shared concurrently.\n///\n/// ## Example\n///\n/// ```no_compile\n/// use rusteron_code_gen::Handler;\n/// let handler = Handler::new(your_value);\n/// // the value is freed when the last clone of `handler` goes out of scope\n/// ```\npub struct Handler<T> {\n    inner: std::sync::Arc<UnsafeCell<T>>,\n}\n\n// Arc\'s refcount is atomic, so moving a Handler (or a clone) to another thread is fine\n// as long as T itself is Send. No Sync: the C conductor thread may call into T via the\n// raw clientd pointer, so shared &Handler across threads would race on T.\nunsafe impl<T: Send> Send for Handler<T> {}\n\n/// Under the `multi-threaded` feature, `Handler` is also `Sync` so callbacks can\n/// be registered from one thread and the handle shared across threads. The\n/// underlying `Arc<UnsafeCell<T>>` is `!Sync` by construction; this impl follows\n/// the same \"accepted unsoundness\" policy as the handle-type impls \u{2014} callbacks\n/// fire from the conductor thread only, never concurrently.\n#[cfg(feature = \"multi-threaded\")]\nunsafe impl<T: Send> Sync for Handler<T> {}\n\nimpl<T> Clone for Handler<T> {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n        }\n    }\n}\n\n/// Utility method for setting empty handlers\npub struct Handlers;\n\n/// Type-level \"no callback\" sentinel.\n///\n/// Pass [`Handlers::NONE`] (which is `None::<&Handler<NoHandler>>`) to any\n/// callback-accepting method to leave that callback unset. `NoHandler` implements\n/// every generated callback trait, so the method\'s callback generic is inferred as\n/// `NoHandler` without a per-callback helper or turbofish \u{2014} including methods with\n/// several callback parameters (e.g. `async_add_subscription`\'s image handlers).\n/// Its callback methods are unreachable: the C side is handed a null callback +\n/// null clientd, so they can never fire.\npub struct NoHandler;\n\nimpl Handlers {\n    /// `None` for any callback parameter \u{2014} pins the callback generic to\n    /// [`NoHandler`] so type inference works without a per-callback helper or\n    /// turbofish. Replaces `Handlers::no_available_image_handler()` /\n    /// `no_unavailable_image_handler()` / `no_reserved_value_supplier_handler()`\n    /// / \u{2026} with one constant. Parallels `Option::None`.\n    pub const NONE: Option<&\'static Handler<NoHandler>> = None;\n}\n\nimpl<T> Handler<T> {\n    pub fn new(handler: T) -> Self {\n        let inner = std::sync::Arc::new(UnsafeCell::new(handler));\n        #[cfg(feature = \"extra-logging\")]\n        log::info!(\"creating handler {:?}\", inner.get());\n        Self { inner }\n    }\n\n    #[inline(always)]\n    pub fn as_raw(&self) -> *mut std::os::raw::c_void {\n        self.inner.get() as *mut std::os::raw::c_void\n    }\n\n    /// Get a mutable reference to the inner value.\n    ///\n    /// # Safety\n    /// Caller must ensure that no other references to the inner value are active.\n    #[inline(always)]\n    pub unsafe fn get_mut(&self) -> &mut T {\n        &mut *self.inner.get()\n    }\n}\n\nimpl<T> Deref for Handler<T> {\n    type Target = T;\n\n    #[inline(always)]\n    fn deref(&self) -> &Self::Target {\n        unsafe { &*self.inner.get() }\n    }\n}\n\npub fn find_unused_udp_port(start_port: u16) -> Option<u16> {\n    let end_port = u16::MAX;\n\n    for port in start_port..=end_port {\n        if is_udp_port_available(port) {\n            return Some(port);\n        }\n    }\n\n    None\n}\n\npub fn is_udp_port_available(port: u16) -> bool {\n    std::net::UdpSocket::bind((\"127.0.0.1\", port)).is_ok()\n}\n\n/// Represents the Aeron URI parser and handler.\npub struct ChannelUri {}\n\nimpl ChannelUri {\n    pub const AERON_SCHEME: &\'static str = \"aeron\";\n    pub const SPY_QUALIFIER: &\'static str = \"aeron-spy\";\n    pub const MAX_URI_LENGTH: usize = 4095;\n\n    /// Return `channel` with a `session-id` param added (replacing any existing one).\n    ///\n    /// Mirrors Java\'s `ChannelUri.addSessionId` \u{2014} the standard way to build a channel\n    /// that joins a specific session, e.g. when subscribing to an archive replay:\n    ///\n    /// ```\n    /// # use rusteron_code_gen::ChannelUri;\n    /// assert_eq!(\n    ///     ChannelUri::add_session_id(\"aeron:ipc\", 42),\n    ///     \"aeron:ipc?session-id=42\"\n    /// );\n    /// assert_eq!(\n    ///     ChannelUri::add_session_id(\"aeron:udp?endpoint=localhost:20121\", -123),\n    ///     \"aeron:udp?endpoint=localhost:20121|session-id=-123\"\n    /// );\n    /// ```\n    pub fn add_session_id(channel: &str, session_id: i32) -> String {\n        Self::set_param(channel, \"session-id\", &session_id.to_string())\n    }\n\n    /// Return `channel` with URI param `key=value` set, replacing an existing `key`\n    /// param if present. Other params keep their relative order; `key` goes last.\n    pub fn set_param(channel: &str, key: &str, value: &str) -> String {\n        let (base, params) = match channel.split_once(\'?\') {\n            None => (channel, \"\"),\n            Some((base, params)) => (base, params),\n        };\n        let mut out = String::with_capacity(channel.len() + key.len() + value.len() + 2);\n        out.push_str(base);\n        out.push(\'?\');\n        for param in params.split(\'|\') {\n            if param.is_empty() || param.split(\'=\').next() == Some(key) {\n                continue;\n            }\n            out.push_str(param);\n            out.push(\'|\');\n        }\n        out.push_str(key);\n        out.push(\'=\');\n        out.push_str(value);\n        out\n    }\n}\n\npub const DRIVER_TIMEOUT_MS_DEFAULT: u64 = 10_000;\npub const AERON_DIR_PROP_NAME: &str = \"aeron.dir\";\npub const AERON_IPC_MEDIA: &str = \"aeron:ipc\";\npub const AERON_UDP_MEDIA: &str = \"aeron:udp\";\npub const SPY_PREFIX: &str = \"aeron-spy:\";\npub const TAG_PREFIX: &str = \"tag:\";\n\n/// Enum for media types.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub enum Media {\n    Ipc,\n    Udp,\n}\n\nimpl Media {\n    pub fn as_str(&self) -> &\'static str {\n        match self {\n            Media::Ipc => \"ipc\",\n            Media::Udp => \"udp\",\n        }\n    }\n}\n\n/// Enum for control modes.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub enum ControlMode {\n    Manual,\n    Dynamic,\n    /// this is a beta feature useful when dealing with docker containers and networking\n    Response,\n}\n\nimpl ControlMode {\n    pub fn as_str(&self) -> &\'static str {\n        match self {\n            ControlMode::Manual => \"manual\",\n            ControlMode::Dynamic => \"dynamic\",\n            ControlMode::Response => \"response\",\n        }\n    }\n}\n\n#[cfg(test)]\n#[allow(dead_code)]\npub(crate) mod test_alloc {\n    use std::alloc::{GlobalAlloc, Layout, System};\n    use std::env;\n    use std::fs::OpenOptions;\n    #[allow(unused_imports)]\n    use std::os::unix::fs::OpenOptionsExt;\n    use std::sync::atomic::{AtomicIsize, Ordering};\n\n    /// A simple global allocator that tracks the net allocation count.\n    /// Used mainly for testing memory leaks or unintended allocations.\n    pub struct TrackingAllocator {\n        allocs: AtomicIsize,\n    }\n\n    impl TrackingAllocator {\n        pub const fn new() -> Self {\n            Self {\n                allocs: AtomicIsize::new(0),\n            }\n        }\n        pub fn current(&self) -> isize {\n            self.allocs.load(Ordering::SeqCst)\n        }\n    }\n\n    unsafe impl GlobalAlloc for TrackingAllocator {\n        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n            self.allocs.fetch_add(1, Ordering::SeqCst);\n            System.alloc(layout)\n        }\n        unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {\n            self.allocs.fetch_sub(1, Ordering::SeqCst);\n            System.dealloc(ptr, layout)\n        }\n        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {\n            self.allocs.fetch_add(1, Ordering::SeqCst);\n            System.alloc_zeroed(layout)\n        }\n        unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {\n            System.realloc(ptr, layout, new_size)\n        }\n    }\n\n    #[global_allocator]\n    static GLOBAL: TrackingAllocator = TrackingAllocator::new();\n\n    /// Returns the current number of net allocations\n    pub fn current_allocs() -> isize {\n        GLOBAL.current()\n    }\n\n    /// Asserts that no allocations occur within the provided closure.\n    /// Uses a file lock to ensure exclusive access across threads/tests.\n    pub fn assert_no_allocation<F: FnOnce()>(f: F) {\n        let tmp = env::temp_dir().join(\"rusteron_allocation.lck\");\n\n        #[cfg(unix)]\n        let file = {\n            OpenOptions::new()\n                .read(true)\n                .write(true)\n                .create(true)\n                .mode(0o600)\n                .open(&tmp)\n                .expect(\"Failed to open allocation lock file\")\n        };\n        #[cfg(not(unix))]\n        let file = {\n            OpenOptions::new()\n                .read(true)\n                .write(true)\n                .create(true)\n                .open(&tmp)\n                .expect(\"Failed to open allocation lock file\")\n        };\n\n        let mut lock = fd_lock::RwLock::new(file);\n        let lock = lock.write().expect(\"Failed to acquire file lock\");\n\n        // Background threads from earlier #[serial] tests (driver/conductor shutdown,\n        // captured log buffers) can allocate or free during our window and produce\n        // spurious deltas. Take the baseline only once the global count is stable.\n        let mut before = current_allocs();\n        let settle_deadline = std::time::Instant::now() + std::time::Duration::from_secs(2);\n        loop {\n            std::thread::sleep(std::time::Duration::from_millis(5));\n            let now = current_allocs();\n            if now == before || std::time::Instant::now() > settle_deadline {\n                before = now;\n                break;\n            }\n            before = now;\n        }\n\n        f();\n        let after = current_allocs();\n        let diff = (after - before).abs();\n        assert!(\n            diff < 50,\n            \"Expected no allocation leak, but alloc count changed from {} to {} (diff {})\",\n            before,\n            after,\n            diff\n        );\n\n        drop(lock)\n    }\n}\n\n/// `format!` for C strings: builds the formatted [`String`] and converts it to a\n/// [`CString`](std::ffi::CString) in one visibly-named step.\n///\n/// This is the recommended way to build **dynamic** channel URIs and other C-string\n/// arguments. The `c`-prefix keeps the heap allocation greppable and visible at the\n/// call site \u{2014} important for latency-sensitive code review \u{2014} while removing the\n/// `format!(...).into_c_string()` noise:\n///\n/// ```\n/// # use rusteron_code_gen::cformat;\n/// let port = 4040;\n/// let uri = cformat!(\"aeron:udp?endpoint=localhost:{port}\");\n/// assert_eq!(uri.to_bytes(), b\"aeron:udp?endpoint=localhost:4040\");\n/// ```\n///\n/// The three-tier pattern for C-string arguments (cheapest first):\n/// 1. **`c\"aeron:ipc\"` literals** \u{2014} compile-time `&\'static CStr`, zero runtime cost.\n///    Use for every constant channel/name.\n/// 2. **`cformat!(...)`** \u{2014} one heap allocation (the formatted `String`; `CString::new`\n///    reuses its buffer). Use for dynamic URIs built once per stream/reconnect.\n/// 3. **Reuse** \u{2014} build the `CString` once, store it, pass `&it` on every call\n///    (zero-copy via `&CString \u{2192} &CStr` deref). Use for anything on a repeated path.\n///\n/// Panics if the formatted string contains an interior nul byte.\n#[macro_export]\nmacro_rules! cformat {\n    ($($arg:tt)*) => {\n        ::std::ffi::CString::new(::std::format!($($arg)*))\n            .expect(\"nul byte in cformat! string\")\n    };\n}\n\npub trait IntoCString {\n    fn into_c_string(self) -> std::ffi::CString;\n}\n\nimpl IntoCString for std::ffi::CString {\n    fn into_c_string(self) -> std::ffi::CString {\n        self\n    }\n}\n\nimpl IntoCString for &str {\n    fn into_c_string(self) -> std::ffi::CString {\n        #[cfg(feature = \"extra-logging\")]\n        log::info!(\"created c string on heap: {:?}\", self);\n\n        std::ffi::CString::new(self).expect(\"failed to create CString\")\n    }\n}\n\nimpl IntoCString for String {\n    fn into_c_string(self) -> std::ffi::CString {\n        #[cfg(feature = \"extra-logging\")]\n        log::info!(\"created c string on heap: {:?}\", self);\n\n        std::ffi::CString::new(self).expect(\"failed to create CString\")\n    }\n}\n\n#[cfg(test)]\nmod handler_tests {\n    use super::*;\n\n    #[test]\n    fn clones_share_the_same_clientd_pointer() {\n        let handler = Handler::new(42u32);\n        let clone = handler.clone();\n        // C receives the same clientd pointer regardless of which clone\n        // registered it, so callbacks always see the same value.\n        assert_eq!(handler.as_raw(), clone.as_raw());\n        assert_eq!(*handler, 42);\n    }\n\n    #[test]\n    fn value_dropped_exactly_once_when_last_clone_drops() {\n        use std::sync::atomic::{AtomicUsize, Ordering};\n        static DROPS: AtomicUsize = AtomicUsize::new(0);\n        struct Counted;\n        impl Drop for Counted {\n            fn drop(&mut self) {\n                DROPS.fetch_add(1, Ordering::SeqCst);\n            }\n        }\n\n        let handler = Handler::new(Counted);\n        let clone = handler.clone();\n        drop(handler);\n        assert_eq!(DROPS.load(Ordering::SeqCst), 0, \"value must outlive remaining clones\");\n        drop(clone);\n        assert_eq!(DROPS.load(Ordering::SeqCst), 1, \"value freed exactly once on last drop\");\n    }\n}\n\n#[cfg(test)]\nmod managed_c_resource_lifecycle_tests {\n    use super::*;\n\n    // These tests pin down `manual_close_required` \u{2014} the field that powers the\n    // Drop-time leak warning. The exact `AeronCncMetadata::load_from_file` bug\n    // was a `new(_, None, false)` resource, so the first test asserts that\n    // construction shape trips the flag. The resource pointers are dummies\n    // (never dereferenced); only the field value is checked.\n\n    #[test]\n    #[cfg(not(feature = \"strict-lifecycle\"))] // would panic on drop under strict-lifecycle\n    fn manual_close_required_true_for_none_cleanup_no_struct() {\n        // The bug shape: owned, no cleanup closure, no Rust struct ownership.\n        let r: ManagedCResource<u8> = ManagedCResource::new(\n            |ctx| {\n                unsafe { *ctx = 0x1 as *mut u8 };\n                1\n            },\n            None,\n            false,\n        )\n        .unwrap_or_else(|e| panic!(\"init failed: code {}\", e.code));\n        assert!(\n            r.manual_close_required,\n            \"owned + None cleanup + no struct ownership must require manual close\"\n        );\n    }\n\n    #[test]\n    fn manual_close_required_false_when_cleanup_closure_present() {\n        let r: ManagedCResource<u8> = ManagedCResource::new(\n            |ctx| {\n                unsafe { *ctx = 0x1 as *mut u8 };\n                1\n            },\n            Some(Box::new(|_ctx| 0)),\n            false,\n        )\n        .unwrap_or_else(|e| panic!(\"init failed: code {}\", e.code));\n        assert!(\n            !r.manual_close_required,\n            \"real cleanup closure means Drop frees the resource \u{2014} no warning needed\"\n        );\n    }\n\n    #[test]\n    fn manual_close_required_false_when_struct_owned() {\n        // Generated `new(_, None, true)` resources: Rust frees the Box itself\n        // via Box::from_raw in the cleanup_struct branch of Drop.\n        let r: ManagedCResource<u8> = ManagedCResource::new(\n            |ctx| {\n                unsafe { *ctx = Box::into_raw(Box::new(0u8)) };\n                1\n            },\n            None,\n            true,\n        )\n        .unwrap_or_else(|e| panic!(\"init failed: code {}\", e.code));\n        assert!(\n            !r.manual_close_required,\n            \"cleanup_struct=true means Rust owns and frees the struct \u{2014} no warning needed\"\n        );\n    }\n}\n";