velesdb_core/
alloc_guard.rs

1//! RAII guards for safe manual memory management.
2//!
3//! # PERF-002: Allocation Guard
4//!
5//! Provides panic-safe allocation patterns for code that must use
6//! manual memory management (e.g., cache-aligned buffers).
7//!
8//! # Usage
9//!
10//! ```rust,ignore
11//! use velesdb_core::alloc_guard::AllocGuard;
12//! use std::alloc::Layout;
13//!
14//! let layout = Layout::from_size_align(1024, 64).unwrap();
15//! let guard = AllocGuard::new(layout)?;
16//!
17//! // Use guard.as_ptr() for operations...
18//! // If panic occurs, memory is automatically freed
19//!
20//! // Transfer ownership when done
21//! let ptr = guard.into_raw();
22//! ```
23
24use std::alloc::{alloc, dealloc, Layout};
25use std::ptr::NonNull;
26
27/// RAII guard for raw allocations.
28///
29/// Ensures memory is deallocated if dropped, preventing leaks on panic.
30/// Use `into_raw()` to take ownership and prevent deallocation.
31#[derive(Debug)]
32pub struct AllocGuard {
33    ptr: NonNull<u8>,
34    layout: Layout,
35    /// If true, memory will be deallocated on drop
36    owns_memory: bool,
37}
38
39impl AllocGuard {
40    /// Allocates memory with the given layout.
41    ///
42    /// # Returns
43    ///
44    /// - `Some(guard)` if allocation succeeded
45    /// - `None` if allocation failed (OOM) or layout size is zero
46    ///
47    /// # Panics
48    ///
49    /// This method does not panic. However, callers typically use
50    /// `unwrap_or_else(|| panic!(...))` which will panic on OOM.
51    ///
52    /// # Safety
53    ///
54    /// The returned guard manages raw memory. The caller must ensure
55    /// proper initialization before use.
56    #[must_use]
57    pub fn new(layout: Layout) -> Option<Self> {
58        if layout.size() == 0 {
59            return None;
60        }
61
62        // SAFETY: Layout is valid (non-zero size)
63        let ptr = unsafe { alloc(layout) };
64
65        NonNull::new(ptr).map(|ptr| Self {
66            ptr,
67            layout,
68            owns_memory: true,
69        })
70    }
71
72    /// Returns the raw pointer to the allocated memory.
73    #[inline]
74    #[must_use]
75    pub fn as_ptr(&self) -> *mut u8 {
76        self.ptr.as_ptr()
77    }
78
79    /// Returns the layout used for this allocation.
80    #[inline]
81    #[must_use]
82    pub fn layout(&self) -> Layout {
83        self.layout
84    }
85
86    /// Transfers ownership of the memory, preventing deallocation on drop.
87    ///
88    /// # Returns
89    ///
90    /// The raw pointer to the allocated memory. The caller is now
91    /// responsible for deallocating it with the same layout.
92    #[inline]
93    #[must_use]
94    pub fn into_raw(mut self) -> *mut u8 {
95        self.owns_memory = false;
96        self.ptr.as_ptr()
97    }
98
99    /// Casts the pointer to a specific type.
100    ///
101    /// # Safety
102    ///
103    /// The caller must ensure the layout is compatible with type T.
104    #[inline]
105    #[must_use]
106    pub fn cast<T>(&self) -> *mut T {
107        self.ptr.as_ptr().cast()
108    }
109}
110
111impl Drop for AllocGuard {
112    fn drop(&mut self) {
113        if self.owns_memory {
114            // SAFETY: ptr was allocated with self.layout and we own it
115            unsafe {
116                dealloc(self.ptr.as_ptr(), self.layout);
117            }
118        }
119    }
120}
121
122// AllocGuard is Send if the underlying memory can be sent between threads
123// SAFETY: Raw memory has no thread affinity
124unsafe impl Send for AllocGuard {}
125
126// AllocGuard is NOT Sync - concurrent access to raw memory is unsafe
127// (intentionally not implementing Sync)