iceoryx2_bb_posix/
memory.rs

1// Copyright (c) 2023 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13//! Provides an interface for low-level heap allocations.
14
15pub use core::alloc::Layout;
16pub use core::ptr::NonNull;
17
18use iceoryx2_bb_elementary::math::*;
19use iceoryx2_bb_elementary_traits::allocator::{
20    AllocationError, AllocationGrowError, AllocationShrinkError,
21};
22use iceoryx2_pal_posix::posix::errno::Errno;
23use iceoryx2_pal_posix::*;
24
25impl From<MemoryError> for AllocationError {
26    fn from(v: MemoryError) -> Self {
27        match v {
28            MemoryError::SizeIsZero => AllocationError::SizeIsZero,
29            MemoryError::OutOfMemory => AllocationError::OutOfMemory,
30            MemoryError::AlignmentFailure => AllocationError::AlignmentFailure,
31            MemoryError::UnknownError(_) => AllocationError::InternalError,
32        }
33    }
34}
35
36impl From<MemoryError> for AllocationGrowError {
37    fn from(v: MemoryError) -> Self {
38        match v {
39            MemoryError::SizeIsZero => AllocationGrowError::SizeIsZero,
40            MemoryError::OutOfMemory => AllocationGrowError::OutOfMemory,
41            MemoryError::AlignmentFailure => AllocationGrowError::AlignmentFailure,
42            MemoryError::UnknownError(_) => AllocationGrowError::InternalError,
43        }
44    }
45}
46
47impl From<MemoryError> for AllocationShrinkError {
48    fn from(v: MemoryError) -> Self {
49        match v {
50            MemoryError::SizeIsZero => AllocationShrinkError::SizeIsZero,
51            MemoryError::AlignmentFailure => AllocationShrinkError::AlignmentFailure,
52            _ => AllocationShrinkError::InternalError,
53        }
54    }
55}
56
57#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
58pub enum MemoryError {
59    OutOfMemory,
60    SizeIsZero,
61    AlignmentFailure,
62    UnknownError(i32),
63}
64
65/// Performs heap allocations. Basis for a heap allocator.
66pub mod heap {
67    use iceoryx2_log::fail;
68
69    use super::*;
70
71    use super::MemoryError;
72    const MEMORY_START_STORAGE_SPACE: usize = core::mem::size_of::<usize>();
73
74    fn setup_and_align(
75        msg: &str,
76        addr: usize,
77        layout: Layout,
78    ) -> Result<NonNull<[u8]>, MemoryError> {
79        if addr == 0 {
80            handle_errno!(MemoryError, from "heap::setup_and_align",
81                Errno::ENOMEM => (OutOfMemory, "{} a size of {} and an alignment of {} due to insufficient memory.", msg, layout.size(), layout.align() ),
82                v => (UnknownError(v as i32), "{} a size of {} and an alignment of {} since an unknown error occurred ({}).", msg, layout.size(), layout.align(), v)
83
84            );
85        }
86
87        let aligned_start = align(addr + MEMORY_START_STORAGE_SPACE, layout.align());
88        unsafe { *(aligned_start as *mut usize).offset(-1) = addr };
89
90        Ok(NonNull::new(core::ptr::slice_from_raw_parts_mut(
91            aligned_start as *mut u8,
92            layout.size(),
93        ))
94        .unwrap())
95    }
96
97    fn check_precondition(msg: &str, layout: Layout) -> Result<(), MemoryError> {
98        if layout.size() == 0 {
99            fail!(from "heap::check_precondition", with MemoryError::SizeIsZero,
100                "{} a size of {} and an alignment of {} since the size is 0.",
101                msg,
102                layout.size(),
103                layout.align()
104            );
105        }
106        Ok(())
107    }
108
109    unsafe fn extract_address(addr: usize) -> usize {
110        let memory = addr as *const usize;
111        *memory.offset(-1)
112    }
113
114    /// Allocates an aligned piece of memory on the heap with a size specified in layout.
115    pub fn allocate(layout: Layout) -> Result<NonNull<[u8]>, MemoryError> {
116        let msg = "Unable to allocate heap memory with";
117        check_precondition(msg, layout)?;
118
119        let alignment_adjustment_buffer = layout.align() - 1;
120        let unaligned_start = unsafe {
121            posix::malloc(layout.size() + alignment_adjustment_buffer + MEMORY_START_STORAGE_SPACE)
122        } as usize;
123
124        setup_and_align(msg, unaligned_start, layout)
125    }
126
127    /// Allocates an aligned and zeroed piece of memory on the heap with a size specified in layout.
128    pub fn allocate_zeroed(layout: Layout) -> Result<NonNull<[u8]>, MemoryError> {
129        let msg = "Unable to allocate zeroed heap memory with";
130        check_precondition(msg, layout)?;
131
132        let alignment_adjustment_buffer = layout.align() - 1;
133        let unaligned_start = unsafe {
134            posix::calloc(
135                layout.size() + alignment_adjustment_buffer + MEMORY_START_STORAGE_SPACE,
136                1,
137            )
138        } as usize;
139
140        setup_and_align(msg, unaligned_start, layout)
141    }
142
143    /// Resizes the memory to the new_layout. If the space after the current block does not
144    /// suffice resize returns a new pointer and copies to content into the new block. The
145    /// old address does not have to be free'd in this case.
146    ///
147    /// # Safety
148    ///
149    ///  * `ptr` must be allocated before with [`heap::allocate()`] or [`heap::allocate_zeroed()`]
150    ///  * `old_layout` must be the same layout it was either acquired with in [`heap::allocate()`]
151    ///    or [`heap::allocate_zeroed()`] or when it was resized the current layout
152    pub unsafe fn resize(
153        ptr: NonNull<u8>,
154        old_layout: Layout,
155        new_layout: Layout,
156    ) -> Result<NonNull<[u8]>, MemoryError> {
157        let msg = "Unable to resize heap memory to";
158        check_precondition(msg, new_layout)?;
159
160        if old_layout.align() < new_layout.align() {
161            fail!(from "heap::resize", with MemoryError::AlignmentFailure,
162                "{} {} since the new layouts alignment was increased from {} to {}.",
163                msg,
164                new_layout.size(),
165                old_layout.align(),
166                new_layout.align()
167            );
168        }
169
170        let alignment_adjustment_buffer = new_layout.align() - 1;
171
172        let current_unaligned_start = extract_address(ptr.as_ptr() as usize);
173        let new_unaligned_start = posix::realloc(
174            current_unaligned_start as *mut posix::void,
175            new_layout.size() + alignment_adjustment_buffer + MEMORY_START_STORAGE_SPACE,
176        ) as usize;
177
178        setup_and_align(msg, new_unaligned_start, new_layout)
179    }
180
181    /// Deallocates a previously allocated piece of memory.
182    ///
183    /// # Safety
184    ///
185    ///  * `ptr` must be allocated before with [`heap::allocate()`] or [`heap::allocate_zeroed()`]
186    ///  * `old_layout` must be the same layout it was either acquired with in [`heap::allocate()`]
187    ///    or [`heap::allocate_zeroed()`] or when it was resized the current layout
188    ///
189    pub unsafe fn deallocate(ptr: NonNull<u8>, _layout: Layout) {
190        posix::free(extract_address(ptr.as_ptr() as usize) as *mut posix::void)
191    }
192}