Skip to main content

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::{enum_gen, 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
57enum_gen! { MemoryError
58  entry:
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        unsafe {
111            let memory = addr as *const usize;
112            *memory.offset(-1)
113        }
114    }
115
116    /// Allocates an aligned piece of memory on the heap with a size specified in layout.
117    pub fn allocate(layout: Layout) -> Result<NonNull<[u8]>, MemoryError> {
118        let msg = "Unable to allocate heap memory with";
119        check_precondition(msg, layout)?;
120
121        let alignment_adjustment_buffer = layout.align() - 1;
122        let unaligned_start = unsafe {
123            posix::malloc(layout.size() + alignment_adjustment_buffer + MEMORY_START_STORAGE_SPACE)
124        } as usize;
125
126        setup_and_align(msg, unaligned_start, layout)
127    }
128
129    /// Allocates an aligned and zeroed piece of memory on the heap with a size specified in layout.
130    pub fn allocate_zeroed(layout: Layout) -> Result<NonNull<[u8]>, MemoryError> {
131        let msg = "Unable to allocate zeroed heap memory with";
132        check_precondition(msg, layout)?;
133
134        let alignment_adjustment_buffer = layout.align() - 1;
135        let unaligned_start = unsafe {
136            posix::calloc(
137                layout.size() + alignment_adjustment_buffer + MEMORY_START_STORAGE_SPACE,
138                1,
139            )
140        } as usize;
141
142        setup_and_align(msg, unaligned_start, layout)
143    }
144
145    /// Resizes the memory to the new_layout. If the space after the current block does not
146    /// suffice resize returns a new pointer and copies to content into the new block. The
147    /// old address does not have to be free'd in this case.
148    ///
149    /// # Safety
150    ///
151    ///  * `ptr` must be allocated before with [`heap::allocate()`] or [`heap::allocate_zeroed()`]
152    ///  * `old_layout` must be the same layout it was either acquired with in [`heap::allocate()`]
153    ///    or [`heap::allocate_zeroed()`] or when it was resized the current layout
154    pub unsafe fn resize(
155        ptr: NonNull<u8>,
156        old_layout: Layout,
157        new_layout: Layout,
158    ) -> Result<NonNull<[u8]>, MemoryError> {
159        let msg = "Unable to resize heap memory to";
160        check_precondition(msg, new_layout)?;
161
162        if old_layout.align() < new_layout.align() {
163            fail!(from "heap::resize", with MemoryError::AlignmentFailure,
164                "{} {} since the new layouts alignment was increased from {} to {}.",
165                msg,
166                new_layout.size(),
167                old_layout.align(),
168                new_layout.align()
169            );
170        }
171
172        let alignment_adjustment_buffer = new_layout.align() - 1;
173        unsafe {
174            let current_unaligned_start = extract_address(ptr.as_ptr() as usize);
175            let new_unaligned_start = posix::realloc(
176                current_unaligned_start as *mut posix::void,
177                new_layout.size() + alignment_adjustment_buffer + MEMORY_START_STORAGE_SPACE,
178            ) as usize;
179
180            setup_and_align(msg, new_unaligned_start, new_layout)
181        }
182    }
183
184    /// Deallocates a previously allocated piece of memory.
185    ///
186    /// # Safety
187    ///
188    ///  * `ptr` must be allocated before with [`heap::allocate()`] or [`heap::allocate_zeroed()`]
189    ///  * `old_layout` must be the same layout it was either acquired with in [`heap::allocate()`]
190    ///    or [`heap::allocate_zeroed()`] or when it was resized the current layout
191    ///
192    pub unsafe fn deallocate(ptr: NonNull<u8>, _layout: Layout) {
193        unsafe { posix::free(extract_address(ptr.as_ptr() as usize) as *mut posix::void) }
194    }
195}