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)