Skip to main content

iceoryx2_bb_container/string/
polymorphic_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//! String implementation with a polymorphic stateful allocator.
14//!
15//! # Example
16//!
17//! ```no_run
18//! # extern crate iceoryx2_bb_loggers;
19//!
20//! use iceoryx2_bb_elementary_traits::allocator::*;
21//! use iceoryx2_bb_container::string::*;
22//!
23//! # use core::ptr::NonNull;
24//!
25//! # struct SomeAllocator {}
26//!
27//! # impl SomeAllocator {
28//! #     fn new() -> Self {
29//! #          Self {}
30//! #     }
31//! # }
32//! #
33//! # impl BaseAllocator for SomeAllocator {
34//! #     fn allocate(
35//! #         &self,
36//! #         layout: core::alloc::Layout,
37//! #     ) -> Result<NonNull<[u8]>, AllocationError> {
38//! #         todo!()
39//! #     }
40//! #
41//! #     unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: core::alloc::Layout) {
42//! #         todo!()
43//! #     }
44//! # }
45//!
46//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
47//! let allocator = SomeAllocator::new();
48//! let capacity: usize = 123;
49//! let mut my_str =
50//!     PolymorphicString::<SomeAllocator>::new(&allocator, capacity)?;
51//!
52//! my_str.push_bytes(b"all glory to the hypnotoad"); // returns false, when capacity is exceeded
53//! # Ok(())
54//! # }
55//! ```
56
57use alloc::format;
58use core::{
59    alloc::Layout,
60    cmp::Ordering,
61    fmt::{Debug, Display},
62    hash::Hash,
63    mem::MaybeUninit,
64    ops::{Deref, DerefMut},
65};
66
67use iceoryx2_bb_elementary_traits::allocator::{AllocationError, BaseAllocator};
68
69use crate::string::*;
70
71/// Runtime fixed-size string variant with a polymorphic allocator, meaning an
72/// allocator with a state can be attached to the string instead of using a
73/// stateless allocator like the heap-allocator.
74pub struct PolymorphicString<'a, Allocator: BaseAllocator> {
75    data_ptr: *mut MaybeUninit<u8>,
76    len: u64,
77    capacity: u64,
78    allocator: &'a Allocator,
79}
80
81impl<Allocator: BaseAllocator> internal::StringView for PolymorphicString<'_, Allocator> {
82    fn data(&self) -> &[MaybeUninit<u8>] {
83        unsafe { core::slice::from_raw_parts(self.data_ptr, self.capacity() + 1) }
84    }
85
86    unsafe fn data_mut(&mut self) -> &mut [MaybeUninit<u8>] {
87        unsafe { core::slice::from_raw_parts_mut(self.data_ptr, self.capacity() + 1) }
88    }
89
90    unsafe fn set_len(&mut self, len: u64) {
91        self.len = len;
92    }
93}
94
95impl<Allocator: BaseAllocator> Debug for PolymorphicString<'_, Allocator> {
96    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
97        write!(
98            f,
99            "PolymorphicString::<{}> {{ capacity: {}, len: {}, data: \"{}\" }}",
100            core::any::type_name::<Allocator>(),
101            self.capacity,
102            self.len,
103            as_escaped_string(self.as_bytes())
104        )
105    }
106}
107
108unsafe impl<Allocator: BaseAllocator> Send for PolymorphicString<'_, Allocator> {}
109
110impl<Allocator: BaseAllocator> PartialOrd<PolymorphicString<'_, Allocator>>
111    for PolymorphicString<'_, Allocator>
112{
113    fn partial_cmp(&self, other: &PolymorphicString<'_, Allocator>) -> Option<Ordering> {
114        Some(self.cmp(other))
115    }
116}
117
118impl<Allocator: BaseAllocator> Ord for PolymorphicString<'_, Allocator> {
119    fn cmp(&self, other: &Self) -> Ordering {
120        self.as_bytes().cmp(other.as_bytes())
121    }
122}
123
124impl<Allocator: BaseAllocator> Hash for PolymorphicString<'_, Allocator> {
125    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
126        state.write(self.as_bytes())
127    }
128}
129
130impl<Allocator: BaseAllocator> Deref for PolymorphicString<'_, Allocator> {
131    type Target = [u8];
132
133    fn deref(&self) -> &Self::Target {
134        self.as_bytes()
135    }
136}
137
138impl<Allocator: BaseAllocator> DerefMut for PolymorphicString<'_, Allocator> {
139    fn deref_mut(&mut self) -> &mut Self::Target {
140        self.as_mut_bytes()
141    }
142}
143
144impl<Allocator: BaseAllocator> PartialEq<PolymorphicString<'_, Allocator>>
145    for PolymorphicString<'_, Allocator>
146{
147    fn eq(&self, other: &PolymorphicString<'_, Allocator>) -> bool {
148        *self.as_bytes() == *other.as_bytes()
149    }
150}
151
152impl<Allocator: BaseAllocator> Eq for PolymorphicString<'_, Allocator> {}
153
154impl<Allocator: BaseAllocator> PartialEq<&[u8]> for PolymorphicString<'_, Allocator> {
155    fn eq(&self, other: &&[u8]) -> bool {
156        *self.as_bytes() == **other
157    }
158}
159
160impl<Allocator: BaseAllocator> PartialEq<&str> for PolymorphicString<'_, Allocator> {
161    fn eq(&self, other: &&str) -> bool {
162        *self.as_bytes() == *other.as_bytes()
163    }
164}
165
166impl<Allocator: BaseAllocator> PartialEq<PolymorphicString<'_, Allocator>> for &str {
167    fn eq(&self, other: &PolymorphicString<'_, Allocator>) -> bool {
168        *self.as_bytes() == *other.as_bytes()
169    }
170}
171
172impl<const OTHER_CAPACITY: usize, Allocator: BaseAllocator> PartialEq<[u8; OTHER_CAPACITY]>
173    for PolymorphicString<'_, Allocator>
174{
175    fn eq(&self, other: &[u8; OTHER_CAPACITY]) -> bool {
176        *self.as_bytes() == *other
177    }
178}
179
180impl<const OTHER_CAPACITY: usize, Allocator: BaseAllocator> PartialEq<&[u8; OTHER_CAPACITY]>
181    for PolymorphicString<'_, Allocator>
182{
183    fn eq(&self, other: &&[u8; OTHER_CAPACITY]) -> bool {
184        *self.as_bytes() == **other
185    }
186}
187
188impl<Allocator: BaseAllocator> Display for PolymorphicString<'_, Allocator> {
189    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
190        write!(f, "{}", as_escaped_string(self.as_bytes()))
191    }
192}
193
194impl<'a, Allocator: BaseAllocator> PolymorphicString<'a, Allocator> {
195    /// Creates a new [`PolymorphicString`].
196    pub fn new(allocator: &'a Allocator, capacity: usize) -> Result<Self, AllocationError> {
197        let layout = Layout::array::<MaybeUninit<u8>>(capacity + 1)
198            .expect("Memory size for the array is smaller than isize::MAX");
199        let mut data_ptr = match allocator.allocate(layout) {
200            Ok(ptr) => ptr,
201            Err(e) => {
202                let origin = format!(
203                    "PolymorphicString::<{}>::new(.., {})",
204                    core::any::type_name::<Allocator>(),
205                    capacity
206                );
207                fail!(from origin, with e,
208                    "Failed to create new PolymorphicString due to a failure while allocating memory ({e:?}).");
209            }
210        };
211
212        Ok(Self {
213            data_ptr: unsafe { data_ptr.as_mut() }.as_mut_ptr().cast(),
214            len: 0,
215            capacity: capacity as _,
216            allocator,
217        })
218    }
219
220    /// Same as clone but it can fail when the required memory could not be
221    /// allocated from the [`BaseAllocator`].
222    pub fn try_clone(&self) -> Result<Self, AllocationError> {
223        let layout = Layout::array::<MaybeUninit<u8>>(self.capacity as usize + 1)
224            .expect("Memory size for the array is smaller than isize::MAX");
225
226        let mut data_ptr = match self.allocator.allocate(layout) {
227            Ok(ptr) => ptr,
228            Err(e) => {
229                let origin = format!(
230                    "PolymorphicString::<{}>::try_clone()",
231                    core::any::type_name::<Allocator>(),
232                );
233                fail!(from origin, with e,
234                    "Failed to clone PolymorphicString due to a failure while allocating memory ({e:?}).");
235            }
236        };
237
238        let mut new_self = Self {
239            data_ptr: unsafe { data_ptr.as_mut() }.as_mut_ptr().cast(),
240            len: 0,
241            capacity: self.capacity,
242            allocator: self.allocator,
243        };
244
245        unsafe { new_self.insert_bytes_unchecked(0, self.as_bytes()) };
246        Ok(new_self)
247    }
248}
249
250impl<Allocator: BaseAllocator> String for PolymorphicString<'_, Allocator> {
251    fn capacity(&self) -> usize {
252        self.capacity as usize
253    }
254
255    fn len(&self) -> usize {
256        self.len as usize
257    }
258}