Skip to main content

wg_utils/unsafe/
mem.rs

1//! Memory utilities for highly optimized operations
2//!
3//! # Safety
4//! These functions use unsafe Rust and should be used with extreme caution.
5//! Improper use can lead to undefined behavior, memory corruption, and security vulnerabilities.
6
7use std::alloc::{self, Layout};
8use std::mem;
9use std::ptr;
10
11/// Allocates uninitialized memory with the specified size and alignment.
12///
13/// # Safety
14/// The caller must ensure the returned memory is properly initialized before reading
15/// and must deallocate the memory using `deallocate` when no longer needed.
16///
17/// # Arguments
18/// * `size` - The size in bytes to allocate
19/// * `align` - The memory alignment (must be a power of 2)
20///
21/// # Returns
22/// A pointer to the allocated memory block or null if allocation failed
23pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
24    if size == 0 {
25        return ptr::null_mut();
26    }
27
28    let layout = Layout::from_size_align_unchecked(size, align);
29    alloc::alloc(layout)
30}
31
32/// Deallocates memory previously allocated with `allocate`.
33///
34/// # Safety
35/// The pointer must have been allocated using `allocate` with the same size and alignment.
36/// After this call, the pointer becomes invalid and must not be used.
37pub unsafe fn deallocate(ptr: *mut u8, size: usize, align: usize) {
38    if !ptr.is_null() && size > 0 {
39        let layout = Layout::from_size_align_unchecked(size, align);
40        alloc::dealloc(ptr, layout);
41    }
42}
43
44/// Fast memory copy that uses SIMD instructions when available.
45///
46/// # Safety
47/// The caller must ensure:
48/// * Both source and destination pointers point to valid memory regions
49/// * The memory regions don't overlap
50/// * The memory regions are at least `count` bytes in size
51///
52/// # Arguments
53/// * `dst` - Destination pointer
54/// * `src` - Source pointer  
55/// * `count` - Number of bytes to copy
56pub unsafe fn fast_memcpy(dst: *mut u8, src: *const u8, count: usize) {
57    if count < 32 {
58        for i in 0..count {
59            *dst.add(i) = *src.add(i);
60        }
61        return;
62    }
63
64    let dst_ptr = dst as *mut usize;
65    let src_ptr = src as *const usize;
66    let word_size = mem::size_of::<usize>();
67    let word_count = count / word_size;
68
69    for i in 0..word_count {
70        *dst_ptr.add(i) = *src_ptr.add(i);
71    }
72
73    let remaining_offset = word_count * word_size;
74    for i in 0..(count - remaining_offset) {
75        *dst.add(remaining_offset + i) = *src.add(remaining_offset + i);
76    }
77}
78
79/// Fast memory set that uses SIMD instructions when available.
80///
81/// # Safety
82/// The caller must ensure:
83/// * The destination pointer points to valid memory region
84/// * The memory region is at least `count` bytes in size
85///
86/// # Arguments
87/// * `dst` - Destination pointer
88/// * `value` - Byte value to set
89/// * `count` - Number of bytes to set
90pub unsafe fn fast_memset(dst: *mut u8, value: u8, count: usize) {
91    if count < 32 {
92        for i in 0..count {
93            *dst.add(i) = value;
94        }
95        return;
96    }
97
98    *dst = value;
99
100    let mut i = 1;
101    while i <= count / 2 {
102        ptr::copy_nonoverlapping(dst, dst.add(i), i);
103        i *= 2;
104    }
105
106    if i < count {
107        ptr::copy_nonoverlapping(dst, dst.add(i), count - i);
108    }
109}
110
111/// Zero memory in a way that shouldn't be optimized out by the compiler.
112/// Useful for clearing sensitive information.
113///
114/// # Safety
115/// The caller must ensure:
116/// * The pointer points to valid memory region
117/// * The memory region is at least `count` bytes in size
118///
119/// # Arguments
120/// * `ptr` - Pointer to memory to clear
121/// * `count` - Number of bytes to clear
122pub unsafe fn secure_zero_memory(ptr: *mut u8, count: usize) {
123    for i in 0..count {
124        ptr::write_volatile(ptr.add(i), 0);
125    }
126    std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
127}
128
129/// Reallocates memory block to a new size.
130///
131/// # Safety
132/// The caller must ensure:
133/// * The original pointer was allocated with `allocate`
134/// * The same size and alignment are provided
135/// * Memory is properly initialized before reading
136///
137/// # Arguments
138/// * `ptr` - Pointer to the memory block to reallocate
139/// * `old_size` - Current size of the allocation
140/// * `new_size` - Desired new size
141/// * `align` - Memory alignment (must be a power of 2)
142///
143/// # Returns
144/// A pointer to the reallocated memory block or null if reallocation failed
145pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, new_size: usize, align: usize) -> *mut u8 {
146    if ptr.is_null() {
147        return allocate(new_size, align);
148    }
149
150    if new_size == 0 {
151        deallocate(ptr, old_size, align);
152        return ptr::null_mut();
153    }
154
155    let old_layout = Layout::from_size_align_unchecked(old_size, align);
156    let new_layout = Layout::from_size_align_unchecked(new_size, align);
157
158    alloc::realloc(ptr, old_layout, new_size)
159}
160
161pub struct MemoryBlock {
162    ptr: *mut u8,
163    size: usize,
164    align: usize,
165}
166
167impl MemoryBlock {
168    pub fn new(size: usize, align: usize) -> Option<Self> {
169        unsafe {
170            let ptr = allocate(size, align);
171            if ptr.is_null() {
172                None
173            } else {
174                Some(Self { ptr, size, align })
175            }
176        }
177    }
178
179    pub fn as_ptr(&self) -> *mut u8 {
180        self.ptr
181    }
182
183    pub fn size(&self) -> usize {
184        self.size
185    }
186
187    pub fn fill(&mut self, value: u8) {
188        unsafe {
189            fast_memset(self.ptr, value, self.size);
190        }
191    }
192
193    pub fn resize(&mut self, new_size: usize) -> bool {
194        unsafe {
195            let new_ptr = reallocate(self.ptr, self.size, new_size, self.align);
196            if new_ptr.is_null() {
197                return false;
198            }
199            self.ptr = new_ptr;
200            self.size = new_size;
201            true
202        }
203    }
204
205    pub fn secure_zero(&mut self) {
206        unsafe {
207            secure_zero_memory(self.ptr, self.size);
208        }
209    }
210}
211
212impl Drop for MemoryBlock {
213    fn drop(&mut self) {
214        unsafe {
215            deallocate(self.ptr, self.size, self.align);
216        }
217    }
218}
219
220#[derive(Debug)]
221pub struct MemoryAccess<'a> {
222    ptr: *mut u8,
223    size: usize,
224    _phantom: std::marker::PhantomData<&'a mut [u8]>,
225}
226
227impl<'a> MemoryAccess<'a> {
228    /// Creates a new memory access wrapper from a raw pointer and size.
229    ///
230    /// # Safety
231    /// The caller must ensure the pointer points to valid memory of at least 'size' bytes
232    /// and remains valid for the lifetime 'a.
233    pub unsafe fn new(ptr: *mut u8, size: usize) -> Self {
234        Self {
235            ptr,
236            size,
237            _phantom: std::marker::PhantomData,
238        }
239    }
240
241    /// Reads a value of type T from the offset.
242    ///
243    /// # Panics
244    /// Panics if the read would go out of bounds.
245    pub fn read<T: Copy>(&self, offset: usize) -> T {
246        assert!(
247            offset + mem::size_of::<T>() <= self.size,
248            "Read out of bounds"
249        );
250        unsafe { ptr::read_unaligned(self.ptr.add(offset) as *const T) }
251    }
252
253    /// Writes a value of type T at the offset.
254    ///
255    /// # Panics
256    /// Panics if the write would go out of bounds.
257    pub fn write<T>(&mut self, offset: usize, value: T) {
258        assert!(
259            offset + mem::size_of::<T>() <= self.size,
260            "Write out of bounds"
261        );
262        unsafe {
263            ptr::write_unaligned(self.ptr.add(offset) as *mut T, value);
264        }
265    }
266
267    /// Gets a slice of the memory.
268    ///
269    /// # Panics
270    /// Panics if the slice would go out of bounds.
271    pub fn slice(&self, offset: usize, len: usize) -> &[u8] {
272        assert!(offset + len <= self.size, "Slice out of bounds");
273        unsafe { std::slice::from_raw_parts(self.ptr.add(offset), len) }
274    }
275
276    /// Gets a mutable slice of the memory.
277    ///
278    /// # Panics
279    /// Panics if the slice would go out of bounds.
280    pub fn slice_mut(&mut self, offset: usize, len: usize) -> &mut [u8] {
281        assert!(offset + len <= self.size, "Slice out of bounds");
282        unsafe { std::slice::from_raw_parts_mut(self.ptr.add(offset), len) }
283    }
284}