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}