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