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