safer_ring/buffer/allocation.rs
1//! Memory allocation utilities for pinned buffers.
2//!
3//! This module provides specialized buffer allocation functions optimized for
4//! Direct Memory Access (DMA) operations and io_uring. The allocators ensure
5//! proper memory alignment and zero-initialization for safe kernel interactions.
6//!
7//! # Key Features
8//!
9//! - Page-aligned allocation for optimal DMA performance
10//! - Zero-initialized memory for security
11//! - Custom alignment support for specialized use cases
12//! - Fallback strategies for allocation failures
13//!
14//! # Examples
15//!
16//! ```
17//! use safer_ring::buffer::allocation::{allocate_aligned_buffer, allocate_with_alignment};
18//!
19//! // Allocate a page-aligned buffer for DMA operations
20//! let buffer = allocate_aligned_buffer(8192);
21//! assert_eq!(buffer.len(), 8192);
22//!
23//! // Allocate with custom alignment
24//! let aligned_buffer = allocate_with_alignment(1024, 64);
25//! assert_eq!(aligned_buffer.len(), 1024);
26//! ```
27
28use std::alloc::{alloc_zeroed, Layout};
29
30/// Allocates a zero-initialized buffer with optimal alignment for DMA operations.
31///
32/// This function attempts to allocate memory with page-aligned (4096 byte) alignment
33/// for optimal performance with Direct Memory Access operations. If page alignment
34/// fails, it falls back to natural byte alignment. The allocated memory is
35/// zero-initialized for security.
36///
37/// # Parameters
38///
39/// * `size` - The size in bytes of the buffer to allocate. If 0, returns an empty slice.
40///
41/// # Returns
42///
43/// Returns a `Box<[u8]>` containing the allocated zero-initialized buffer.
44/// The buffer will be page-aligned (4096 bytes) when possible, or naturally
45/// aligned as a fallback.
46///
47/// # Panics
48///
49/// Panics if:
50/// - Memory allocation fails (out of memory)
51/// - The size parameter is too large for the system to handle
52///
53/// # Examples
54///
55/// ```
56/// use safer_ring::buffer::allocation::allocate_aligned_buffer;
57///
58/// // Allocate an 8KB buffer
59/// let buffer = allocate_aligned_buffer(8192);
60/// assert_eq!(buffer.len(), 8192);
61/// assert!(buffer.iter().all(|&b| b == 0)); // All zeros
62///
63/// // Empty buffer case
64/// let empty = allocate_aligned_buffer(0);
65/// assert_eq!(empty.len(), 0);
66/// ```
67pub fn allocate_aligned_buffer(size: usize) -> Box<[u8]> {
68 if size == 0 {
69 return Box::new([]);
70 }
71
72 // Try page-aligned allocation first for better DMA performance
73 let layout = Layout::from_size_align(size, 4096).unwrap_or_else(|_| {
74 // Fall back to natural alignment if page alignment fails
75 Layout::from_size_align(size, std::mem::align_of::<u8>())
76 .expect("Failed to create layout for buffer allocation")
77 });
78
79 unsafe {
80 let ptr = alloc_zeroed(layout);
81 if ptr.is_null() {
82 panic!("Failed to allocate aligned buffer of size {size}");
83 }
84
85 // Convert raw pointer to boxed slice
86 let slice = std::slice::from_raw_parts_mut(ptr, size);
87 Box::from_raw(slice)
88 }
89}
90
91/// Allocates a buffer with specific alignment requirements.
92///
93/// This function allocates zero-initialized memory with a custom alignment
94/// requirement. Unlike `allocate_aligned_buffer`, this function allows you to
95/// specify the exact alignment needed, which is useful for specialized hardware
96/// requirements or performance optimizations.
97///
98/// # Parameters
99///
100/// * `size` - The size in bytes of the buffer to allocate. If 0, returns an empty slice.
101/// * `align` - The required alignment in bytes. Must be a power of 2.
102///
103/// # Returns
104///
105/// Returns a `Box<[u8]>` containing the allocated zero-initialized buffer
106/// aligned to the specified boundary.
107///
108/// # Panics
109///
110/// Panics if:
111/// - The `align` parameter is not a power of 2
112/// - The `size` and `align` combination is invalid
113/// - Memory allocation fails (out of memory)
114/// - The alignment requirement cannot be satisfied
115///
116/// # Examples
117///
118/// ```
119/// use safer_ring::buffer::allocation::allocate_with_alignment;
120///
121/// // Allocate 1KB buffer aligned to 64-byte boundary (for cache line alignment)
122/// let buffer = allocate_with_alignment(1024, 64);
123/// assert_eq!(buffer.len(), 1024);
124/// assert!(buffer.iter().all(|&b| b == 0)); // All zeros
125///
126/// // Allocate with 16-byte alignment
127/// let aligned = allocate_with_alignment(256, 16);
128/// assert_eq!(aligned.len(), 256);
129///
130/// // Empty buffer case
131/// let empty = allocate_with_alignment(0, 32);
132/// assert_eq!(empty.len(), 0);
133/// ```
134pub fn allocate_with_alignment(size: usize, align: usize) -> Box<[u8]> {
135 if size == 0 {
136 return Box::new([]);
137 }
138
139 let layout = Layout::from_size_align(size, align).expect("Invalid size/alignment combination");
140
141 unsafe {
142 let ptr = alloc_zeroed(layout);
143 if ptr.is_null() {
144 panic!("Failed to allocate buffer of size {size} with alignment {align}");
145 }
146
147 let slice = std::slice::from_raw_parts_mut(ptr, size);
148 Box::from_raw(slice)
149 }
150}