ps_alloc/realloc.rs
1use std::ptr::{copy_nonoverlapping, null_mut};
2
3use crate::{
4 header::{AllocationHeader, HEADER_SIZE},
5 ReallocationError,
6};
7
8/// Reallocates memory allocated by [`crate::alloc`].
9///
10/// # Errors
11///
12/// - `AllocationError` is returned if the call to [`crate::alloc`] fails.
13/// - `DeallocationError` is returned if the call to [`crate::free`] fails.
14/// - `ImproperAlignment` is returned if the pointer provided was not aligned properly.
15/// - `MarkerFree` is returned if `ptr` was previously freed or reallocated.
16/// - `MarkerCorrupted` is returned if the marker is neither free nor used, which generally signals memory corruption, or the passing of a pointer not allocated by [`crate::alloc`].
17/// - `NewAllocationFailed` is returned if allocating the new memory buffer fails. This is the only error which is fully recoverable, as your current allocation remains valid.
18///
19/// # Safety
20///
21/// See [`crate::free`] for safety information.
22pub unsafe fn realloc(ptr: *mut u8, new_size: usize) -> Result<*mut u8, ReallocationError> {
23 if ptr.is_null() {
24 return Ok(crate::alloc(new_size)?);
25 }
26
27 if !(ptr as usize).is_multiple_of(HEADER_SIZE) {
28 return Err(ReallocationError::ImproperAlignment);
29 }
30
31 let header_ptr: *mut AllocationHeader = ptr.sub(HEADER_SIZE).cast();
32
33 if !header_ptr.is_aligned() {
34 return Err(ReallocationError::ImproperAlignment);
35 }
36
37 if (*header_ptr).is_marked_as_free() {
38 return Err(ReallocationError::MarkerFree);
39 }
40
41 if !(*header_ptr).is_marked_as_used() {
42 return Err(ReallocationError::MarkerCorrupted);
43 }
44
45 if new_size == 0 {
46 // This is equivalent to `free` and returns a nullptr.
47
48 crate::free(ptr)?;
49
50 return Ok(null_mut());
51 }
52
53 let Ok(new_ptr) = crate::alloc(new_size) else {
54 return Err(ReallocationError::NewAllocationFailed(ptr));
55 };
56
57 let memset_len = (*header_ptr)
58 // full allocation size, including header
59 .get_size()
60 // don't copy the header
61 .saturating_sub(HEADER_SIZE)
62 // truncate if needed
63 .min(new_size);
64
65 // copy data to new location
66 copy_nonoverlapping(ptr, new_ptr, memset_len);
67
68 // the error branch is unlikely here, since we've already checked the pointer's validity
69 match crate::free(ptr) {
70 Ok(()) => {}
71 Err(err) => {
72 // clean up new allocation
73 crate::free(new_ptr)?;
74
75 // pass the error
76 Err(err)?;
77 }
78 }
79
80 Ok(new_ptr)
81}