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