1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! RAII guards for safe manual memory management.
//!
//! # PERF-002: Allocation Guard
//!
//! Provides panic-safe allocation patterns for code that must use
//! manual memory management (e.g., cache-aligned buffers).
//!
//! # Usage
//!
//! ```rust,ignore
//! use velesdb_core::alloc_guard::AllocGuard;
//! use std::alloc::Layout;
//!
//! let layout = Layout::from_size_align(1024, 64).unwrap();
//! let guard = AllocGuard::new(layout)?;
//!
//! // Use guard.as_ptr() for operations...
//! // If panic occurs, memory is automatically freed
//!
//! // Transfer ownership when done
//! let ptr = guard.into_raw();
//! ```
use std::alloc::{alloc, alloc_zeroed, dealloc, Layout};
use std::ptr::NonNull;
/// RAII guard for raw allocations.
///
/// Ensures memory is deallocated if dropped, preventing leaks on panic.
/// Use `into_raw()` to take ownership and prevent deallocation.
#[derive(Debug)]
pub struct AllocGuard {
ptr: NonNull<u8>,
layout: Layout,
/// If true, memory will be deallocated on drop
owns_memory: bool,
}
impl AllocGuard {
/// Allocates memory with the given layout.
///
/// # Returns
///
/// - `Some(guard)` if allocation succeeded
/// - `None` if allocation failed (OOM) or layout size is zero
///
/// # Panics
///
/// This method does not panic. However, callers typically use
/// `unwrap_or_else(|| panic!(...))` which will panic on OOM.
///
/// # Safety
///
/// The returned guard manages raw memory. The caller must ensure
/// proper initialization before use.
#[must_use]
pub fn new(layout: Layout) -> Option<Self> {
if layout.size() == 0 {
return None;
}
// SAFETY: `alloc` requires a valid non-zero layout.
// - Condition 1: `layout.size() > 0` is checked above.
// - Condition 2: `Layout` comes from std APIs and is therefore well-formed.
// SAFETY: Raw allocation is required to build a panic-safe RAII guard.
let ptr = unsafe { alloc(layout) };
NonNull::new(ptr).map(|ptr| Self {
ptr,
layout,
owns_memory: true,
})
}
/// Allocates zero-initialized memory with the given layout.
///
/// Same as [`new`](Self::new) but guarantees all bytes are zero.
/// Use for buffers where sparse writes (e.g., `insert_at`) may leave gaps.
#[must_use]
pub fn new_zeroed(layout: Layout) -> Option<Self> {
if layout.size() == 0 {
return None;
}
// SAFETY: `alloc_zeroed` requires a valid non-zero layout.
// - Condition 1: `layout.size() > 0` is checked above.
// - Condition 2: `Layout` comes from std APIs and is therefore well-formed.
// SAFETY: Zero-initialized allocation prevents UB from reading unwritten slots.
let ptr = unsafe { alloc_zeroed(layout) };
NonNull::new(ptr).map(|ptr| Self {
ptr,
layout,
owns_memory: true,
})
}
/// Returns the raw pointer to the allocated memory.
#[inline]
#[must_use]
pub fn as_ptr(&self) -> *mut u8 {
self.ptr.as_ptr()
}
/// Returns the layout used for this allocation.
#[inline]
#[must_use]
pub fn layout(&self) -> Layout {
self.layout
}
/// Transfers ownership of the memory, preventing deallocation on drop.
///
/// # Returns
///
/// The raw pointer to the allocated memory. The caller is now
/// responsible for deallocating it with the same layout.
#[inline]
#[must_use]
pub fn into_raw(mut self) -> *mut u8 {
self.owns_memory = false;
self.ptr.as_ptr()
}
/// Casts the pointer to a specific type.
///
/// # Safety
///
/// The caller must ensure the layout is compatible with type T.
#[inline]
#[must_use]
pub fn cast<T>(&self) -> *mut T {
self.ptr.as_ptr().cast()
}
}
impl Drop for AllocGuard {
fn drop(&mut self) {
if self.owns_memory {
// SAFETY: `dealloc` requires the original pointer/layout pair.
// - Condition 1: `self.ptr` was produced by `alloc(self.layout)` in `new`.
// - Condition 2: `owns_memory` guarantees this path runs at most once.
// SAFETY: Manual deallocation is needed for raw-memory RAII.
unsafe {
dealloc(self.ptr.as_ptr(), self.layout);
}
}
}
}
// SAFETY: `AllocGuard` is `Send` because it owns an allocation handle only.
// - Condition 1: No aliasing references are stored, only pointer + layout metadata.
// - Condition 2: Mutation requires `&mut self`, preventing cross-thread races on the type.
// SAFETY: Heap allocations are not thread-affine; ownership transfer across threads is sound.
unsafe impl Send for AllocGuard {}
// AllocGuard is NOT Sync - concurrent access to raw memory is unsafe
// (intentionally not implementing Sync)