Skip to main content

iceoryx2_bb_container/string/
relocatable_string.rs

1// Copyright (c) 2025 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//! Contains the [`RelocatableString`], a
14//! run-time fixed size string that is shared memory compatible
15//!
16//! # Expert Examples
17//!
18//! ## Create [`RelocatableString`] inside construct which provides memory
19//!
20//! ```
21//! # extern crate iceoryx2_bb_loggers;
22//!
23//! use iceoryx2_bb_container::string::*;
24//! use iceoryx2_bb_elementary::math::align_to;
25//! use iceoryx2_bb_elementary::bump_allocator::BumpAllocator;
26//! use core::mem::MaybeUninit;
27//!
28//! const STRING_CAPACITY:usize = 12;
29//! struct MyConstruct {
30//!     my_str: RelocatableString,
31//!     str_memory: [MaybeUninit<u128>; STRING_CAPACITY + 1],
32//! }
33//!
34//! impl MyConstruct {
35//!     pub fn new() -> Self {
36//!         let mut new_self = Self {
37//!             my_str: unsafe { RelocatableString::new_uninit(STRING_CAPACITY) },
38//!             str_memory: [const { MaybeUninit::uninit() }; STRING_CAPACITY + 1] ,
39//!         };
40//!
41//!         let allocator = BumpAllocator::new(
42//!             core::ptr::NonNull::<u8>::new(new_self.str_memory.as_mut_ptr().cast())
43//!                 .expect("Precondition failed: Pointer to memory is null"),
44//!             new_self.str_memory.len()
45//!         );
46//!
47//!         unsafe {
48//!             new_self.my_str.init(&allocator).expect("Enough memory provided.")
49//!         };
50//!         new_self
51//!     }
52//! }
53//! ```
54//!
55//! ## Create [`RelocatableString`] with allocator
56//!
57//! ```
58//! # extern crate iceoryx2_bb_loggers;
59//!
60//! use iceoryx2_bb_container::string::*;
61//! use iceoryx2_bb_elementary::bump_allocator::BumpAllocator;
62//! use core::ptr::NonNull;
63//!
64//! const STRING_CAPACITY:usize = 12;
65//! const MEM_SIZE: usize = RelocatableString::const_memory_size(STRING_CAPACITY);
66//! let mut memory = [0u8; MEM_SIZE];
67//!
68//! let bump_allocator = BumpAllocator::new(
69//!         core::ptr::NonNull::<u8>::new(memory.as_mut_ptr().cast())
70//!             .expect("Precondition failed: Pointer to memory is null"),
71//!         memory.len()
72//!     );
73//!
74//! let mut my_str = unsafe { RelocatableString::new_uninit(STRING_CAPACITY) };
75//! unsafe { my_str.init(&bump_allocator).expect("string init failed") };
76//! ```
77
78use core::alloc::Layout;
79use core::cmp::Ordering;
80use core::fmt::{Debug, Display};
81use core::hash::Hash;
82use core::mem::MaybeUninit;
83use core::ops::Deref;
84use core::ptr::NonNull;
85use iceoryx2_bb_elementary::math::unaligned_mem_size;
86use iceoryx2_bb_elementary::relocatable_pointer::RelocatablePointer;
87use iceoryx2_bb_elementary_traits::pointer::Pointer;
88pub use iceoryx2_bb_elementary_traits::relocatable_container::RelocatableContainer;
89use iceoryx2_log::{fail, fatal_panic};
90
91use crate::string::{String, as_escaped_string, internal};
92
93/// **Non-movable** relocatable shared-memory compatible string with runtime fixed size capacity.
94#[repr(C)]
95pub struct RelocatableString {
96    data_ptr: RelocatablePointer<MaybeUninit<u8>>,
97    capacity: u64,
98    len: u64,
99}
100
101impl internal::StringView for RelocatableString {
102    fn data(&self) -> &[MaybeUninit<u8>] {
103        self.verify_init("data()");
104        unsafe { core::slice::from_raw_parts(self.data_ptr.as_ptr(), self.capacity() + 1) }
105    }
106
107    unsafe fn data_mut(&mut self) -> &mut [MaybeUninit<u8>] {
108        self.verify_init("data_mut()");
109        unsafe { core::slice::from_raw_parts_mut(self.data_ptr.as_mut_ptr(), self.capacity() + 1) }
110    }
111
112    unsafe fn set_len(&mut self, len: u64) {
113        self.len = len;
114    }
115}
116
117impl Debug for RelocatableString {
118    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119        write!(
120            f,
121            "RelocatableString {{ capacity: {}, len: {}, data: \"{}\" }}",
122            self.capacity,
123            self.len,
124            as_escaped_string(self.as_bytes())
125        )
126    }
127}
128
129unsafe impl Send for RelocatableString {}
130
131impl PartialOrd<RelocatableString> for RelocatableString {
132    fn partial_cmp(&self, other: &RelocatableString) -> Option<Ordering> {
133        Some(self.cmp(other))
134    }
135}
136
137impl Ord for RelocatableString {
138    fn cmp(&self, other: &Self) -> Ordering {
139        self.as_bytes().cmp(other.as_bytes())
140    }
141}
142
143impl Hash for RelocatableString {
144    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
145        state.write(self.as_bytes())
146    }
147}
148
149impl Deref for RelocatableString {
150    type Target = [u8];
151
152    fn deref(&self) -> &Self::Target {
153        self.as_bytes()
154    }
155}
156
157impl PartialEq<RelocatableString> for RelocatableString {
158    fn eq(&self, other: &RelocatableString) -> bool {
159        *self.as_bytes() == *other.as_bytes()
160    }
161}
162
163impl Eq for RelocatableString {}
164
165impl PartialEq<&[u8]> for RelocatableString {
166    fn eq(&self, other: &&[u8]) -> bool {
167        *self.as_bytes() == **other
168    }
169}
170
171impl PartialEq<&str> for RelocatableString {
172    fn eq(&self, other: &&str) -> bool {
173        *self.as_bytes() == *other.as_bytes()
174    }
175}
176
177impl PartialEq<RelocatableString> for &str {
178    fn eq(&self, other: &RelocatableString) -> bool {
179        *self.as_bytes() == *other.as_bytes()
180    }
181}
182
183impl<const OTHER_CAPACITY: usize> PartialEq<[u8; OTHER_CAPACITY]> for RelocatableString {
184    fn eq(&self, other: &[u8; OTHER_CAPACITY]) -> bool {
185        *self.as_bytes() == *other
186    }
187}
188
189impl<const OTHER_CAPACITY: usize> PartialEq<&[u8; OTHER_CAPACITY]> for RelocatableString {
190    fn eq(&self, other: &&[u8; OTHER_CAPACITY]) -> bool {
191        *self.as_bytes() == **other
192    }
193}
194
195impl Display for RelocatableString {
196    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
197        write!(f, "{}", as_escaped_string(self.as_bytes()))
198    }
199}
200
201impl RelocatableString {
202    #[inline(always)]
203    fn verify_init(&self, source: &str) {
204        debug_assert!(
205            self.data_ptr.is_initialized(),
206            "From: RelocatableString::{}, Undefined behavior - the object was not initialized with 'init' before.",
207            source
208        );
209    }
210
211    /// Returns the required memory size for a string with a specified capacity
212    pub const fn const_memory_size(capacity: usize) -> usize {
213        unaligned_mem_size::<u8>(capacity + 1)
214    }
215}
216
217impl RelocatableContainer for RelocatableString {
218    unsafe fn new_uninit(capacity: usize) -> Self {
219        Self {
220            data_ptr: unsafe { RelocatablePointer::new_uninit() },
221            capacity: capacity as u64,
222            len: 0,
223        }
224    }
225
226    unsafe fn init<Allocator: iceoryx2_bb_elementary_traits::allocator::Allocate<NonNull<u8>>>(
227        &mut self,
228        allocator: &Allocator,
229    ) -> Result<(), iceoryx2_bb_elementary_traits::allocator::AllocationError> {
230        let origin = "RelocatableString::init()";
231        if self.data_ptr.is_initialized() {
232            fatal_panic!(from origin,
233                "Memory already initialized! Initializing it twice may lead to undefined behavior.");
234        }
235
236        let ptr = match allocator.allocate(unsafe {
237            Layout::from_size_align_unchecked(
238                core::mem::size_of::<u8>() * (self.capacity as usize + 1),
239                core::mem::align_of::<u8>(),
240            )
241        }) {
242            Ok(ptr) => ptr,
243            Err(e) => {
244                fail!(from origin, with e,
245                    "Failed to initialize since the allocation of the data memory failed.");
246            }
247        };
248
249        unsafe {
250            self.data_ptr.init(ptr);
251        }
252        Ok(())
253    }
254
255    fn memory_size(capacity: usize) -> usize {
256        Self::const_memory_size(capacity)
257    }
258}
259
260impl String for RelocatableString {
261    fn capacity(&self) -> usize {
262        self.capacity as usize
263    }
264
265    fn len(&self) -> usize {
266        self.len as usize
267    }
268}