iceoryx2_bb_container/string/
static_string.rs1use alloc::format;
37use core::str::FromStr;
38use core::{
39 cmp::Ordering,
40 fmt::{Debug, Display},
41 hash::Hash,
42 mem::MaybeUninit,
43 ops::{Deref, DerefMut},
44};
45
46use iceoryx2_bb_derive_macros::{PlacementDefault, ZeroCopySend};
47use iceoryx2_bb_elementary_traits::placement_default::PlacementDefault;
48use iceoryx2_bb_elementary_traits::zero_copy_send::ZeroCopySend;
49use iceoryx2_log::fail;
50use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
51
52use crate::string::{
53 as_escaped_string, internal::StringView, strnlen, String, StringModificationError,
54};
55
56#[derive(PlacementDefault, ZeroCopySend, Clone, Copy)]
59#[repr(C)]
60pub struct StaticString<const CAPACITY: usize> {
61 data: [MaybeUninit<u8>; CAPACITY],
62 terminator: u8,
63 len: u64,
64}
65
66impl<const CAPACITY: usize> Serialize for StaticString<CAPACITY> {
67 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
68 where
69 S: Serializer,
70 {
71 serializer.serialize_str(core::str::from_utf8(self.as_bytes()).unwrap())
72 }
73}
74
75struct StaticStringVisitor<const CAPACITY: usize>;
76
77impl<const CAPACITY: usize> Visitor<'_> for StaticStringVisitor<CAPACITY> {
78 type Value = StaticString<CAPACITY>;
79
80 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
81 formatter.write_str(&format!("a string with a length of at most {CAPACITY}"))
82 }
83
84 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
85 where
86 E: serde::de::Error,
87 {
88 match StaticString::from_bytes(v.as_bytes()) {
89 Ok(v) => Ok(v),
90 Err(_) => Err(E::custom(format!(
91 "the string exceeds the maximum length of {CAPACITY}"
92 ))),
93 }
94 }
95}
96
97impl<'de, const CAPACITY: usize> Deserialize<'de> for StaticString<CAPACITY> {
98 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
99 where
100 D: Deserializer<'de>,
101 {
102 deserializer.deserialize_str(StaticStringVisitor)
103 }
104}
105
106unsafe impl<const CAPACITY: usize> Send for StaticString<CAPACITY> {}
107
108impl<const CAPACITY: usize, const CAPACITY_OTHER: usize> PartialOrd<StaticString<CAPACITY_OTHER>>
109 for StaticString<CAPACITY>
110{
111 fn partial_cmp(&self, other: &StaticString<CAPACITY_OTHER>) -> Option<Ordering> {
112 self.as_bytes().partial_cmp(other.as_bytes())
113 }
114}
115
116impl<const CAPACITY: usize> Ord for StaticString<CAPACITY> {
117 fn cmp(&self, other: &Self) -> Ordering {
118 self.as_bytes().cmp(other.as_bytes())
119 }
120}
121
122impl<const CAPACITY: usize> Hash for StaticString<CAPACITY> {
123 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
124 state.write(self.as_bytes())
125 }
126}
127
128impl<const CAPACITY: usize> Deref for StaticString<CAPACITY> {
129 type Target = [u8];
130
131 fn deref(&self) -> &Self::Target {
132 self.as_bytes()
133 }
134}
135
136impl<const CAPACITY: usize> DerefMut for StaticString<CAPACITY> {
137 fn deref_mut(&mut self) -> &mut Self::Target {
138 self.as_mut_bytes()
139 }
140}
141
142impl<const CAPACITY: usize, const OTHER_CAPACITY: usize> PartialEq<StaticString<OTHER_CAPACITY>>
143 for StaticString<CAPACITY>
144{
145 fn eq(&self, other: &StaticString<OTHER_CAPACITY>) -> bool {
146 *self.as_bytes() == *other.as_bytes()
147 }
148}
149
150impl<const CAPACITY: usize> Eq for StaticString<CAPACITY> {}
151
152impl<const CAPACITY: usize> PartialEq<&[u8]> for StaticString<CAPACITY> {
153 fn eq(&self, other: &&[u8]) -> bool {
154 *self.as_bytes() == **other
155 }
156}
157
158impl<const CAPACITY: usize> PartialEq<&str> for StaticString<CAPACITY> {
159 fn eq(&self, other: &&str) -> bool {
160 *self.as_bytes() == *other.as_bytes()
161 }
162}
163
164impl<const CAPACITY: usize> PartialEq<StaticString<CAPACITY>> for &str {
165 fn eq(&self, other: &StaticString<CAPACITY>) -> bool {
166 *self.as_bytes() == *other.as_bytes()
167 }
168}
169
170impl<const CAPACITY: usize, const OTHER_CAPACITY: usize> PartialEq<[u8; OTHER_CAPACITY]>
171 for StaticString<CAPACITY>
172{
173 fn eq(&self, other: &[u8; OTHER_CAPACITY]) -> bool {
174 *self.as_bytes() == *other
175 }
176}
177
178impl<const CAPACITY: usize, const OTHER_CAPACITY: usize> PartialEq<&[u8; OTHER_CAPACITY]>
179 for StaticString<CAPACITY>
180{
181 fn eq(&self, other: &&[u8; OTHER_CAPACITY]) -> bool {
182 *self.as_bytes() == **other
183 }
184}
185
186impl<const CAPACITY: usize> Debug for StaticString<CAPACITY> {
187 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
188 write!(
189 f,
190 "StaticString<{}> {{ len: {}, data: \"{}\" }}",
191 CAPACITY,
192 self.len,
193 as_escaped_string(self.as_bytes())
194 )
195 }
196}
197
198impl<const CAPACITY: usize> Display for StaticString<CAPACITY> {
199 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
200 write!(f, "{}", as_escaped_string(self.as_bytes()))
201 }
202}
203
204impl<const CAPACITY: usize> TryFrom<&str> for StaticString<CAPACITY> {
205 type Error = StringModificationError;
206
207 fn try_from(value: &str) -> Result<Self, Self::Error> {
208 Self::try_from(value.as_bytes())
209 }
210}
211
212impl<const CAPACITY: usize, const N: usize> TryFrom<&[u8; N]> for StaticString<CAPACITY> {
213 type Error = StringModificationError;
214
215 fn try_from(value: &[u8; N]) -> Result<Self, Self::Error> {
216 Self::try_from(value.as_slice())
217 }
218}
219
220impl<const CAPACITY: usize> TryFrom<&[u8]> for StaticString<CAPACITY> {
221 type Error = StringModificationError;
222
223 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
224 if CAPACITY < value.len() {
225 fail!(from "StaticString::from<&[u8]>()",
226 with StringModificationError::InsertWouldExceedCapacity,
227 "The provided string \"{}\" does not fit into the StaticString with capacity {}",
228 as_escaped_string(value), CAPACITY);
229 }
230
231 let mut new_self = Self::new();
232 new_self.push_bytes(value)?;
233 Ok(new_self)
234 }
235}
236
237impl<const CAPACITY: usize> Default for StaticString<CAPACITY> {
238 fn default() -> Self {
239 Self::new()
240 }
241}
242
243impl<const CAPACITY: usize> StringView for StaticString<CAPACITY> {
244 fn data(&self) -> &[MaybeUninit<u8>] {
245 &self.data
246 }
247
248 unsafe fn data_mut(&mut self) -> &mut [MaybeUninit<u8>] {
249 &mut self.data
250 }
251
252 unsafe fn set_len(&mut self, len: u64) {
253 self.len = len
254 }
255}
256
257impl<const CAPACITY: usize> FromStr for StaticString<CAPACITY> {
258 type Err = StringModificationError;
259
260 fn from_str(s: &str) -> Result<Self, StringModificationError> {
261 Self::from_bytes(s.as_bytes())
262 }
263}
264
265impl<const CAPACITY: usize> StaticString<CAPACITY> {
266 pub const fn new() -> Self {
268 let mut new_self = Self {
269 len: 0,
270 data: unsafe { MaybeUninit::uninit().assume_init() },
271 terminator: 0,
272 };
273 new_self.data[0] = MaybeUninit::new(0);
274 new_self
275 }
276
277 pub const unsafe fn from_bytes_unchecked_restricted(bytes: &[u8], len: usize) -> Self {
286 debug_assert!(bytes.len() <= CAPACITY);
287 debug_assert!(len <= bytes.len());
288
289 let mut new_self = Self::new();
290 core::ptr::copy_nonoverlapping(bytes.as_ptr(), new_self.data.as_mut_ptr().cast(), len);
291 core::ptr::write::<u8>(new_self.data.as_mut_ptr().add(len).cast(), 0);
292 new_self.len = len as u64;
293 new_self
294 }
295
296 pub const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
305 debug_assert!(bytes.len() <= CAPACITY);
306
307 Self::from_bytes_unchecked_restricted(bytes, bytes.len())
308 }
309
310 pub fn from_bytes(bytes: &[u8]) -> Result<Self, StringModificationError> {
312 let mut new_self = Self::new();
313 new_self.insert_bytes(0, bytes)?;
314
315 Ok(new_self)
316 }
317
318 pub fn from_bytes_truncated(bytes: &[u8]) -> Result<Self, StringModificationError> {
321 let mut new_self = Self::new();
322 new_self.insert_bytes(0, &bytes[0..core::cmp::min(bytes.len(), CAPACITY)])?;
323 Ok(new_self)
324 }
325
326 pub fn from_str_truncated(s: &str) -> Result<Self, StringModificationError> {
329 Self::from_bytes_truncated(s.as_bytes())
330 }
331
332 pub unsafe fn from_c_str(
340 ptr: *const core::ffi::c_char,
341 ) -> Result<Self, StringModificationError> {
342 let string_length = strnlen(ptr, CAPACITY + 1);
343 if CAPACITY < string_length {
344 return Err(StringModificationError::InsertWouldExceedCapacity);
345 }
346
347 Self::from_bytes(core::slice::from_raw_parts(ptr.cast(), string_length))
348 }
349
350 pub const fn capacity() -> usize {
352 CAPACITY
353 }
354
355 pub const fn as_bytes_const(&self) -> &[u8] {
357 unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), self.len as usize) }
358 }
359}
360
361impl<const CAPACITY: usize> String for StaticString<CAPACITY> {
362 fn capacity(&self) -> usize {
363 CAPACITY
364 }
365
366 fn len(&self) -> usize {
367 self.len as usize
368 }
369}
370
371#[allow(missing_docs)]
372pub struct StringMemoryLayoutMetrics {
373 pub string_size: usize,
374 pub string_alignment: usize,
375 pub size_data: usize,
376 pub offset_data: usize,
377 pub size_len: usize,
378 pub offset_len: usize,
379 pub len_is_unsigned: bool,
380}
381
382trait _StringMemoryLayoutFieldLenInspection {
383 fn is_unsigned(&self) -> bool;
384}
385
386impl _StringMemoryLayoutFieldLenInspection for u64 {
387 fn is_unsigned(&self) -> bool {
388 true
389 }
390}
391
392impl StringMemoryLayoutMetrics {
393 #[allow(missing_docs)]
394 pub fn from_string<const CAPACITY: usize>(v: &StaticString<CAPACITY>) -> Self {
395 StringMemoryLayoutMetrics {
396 string_size: core::mem::size_of_val(v),
397 string_alignment: core::mem::align_of_val(v),
398 size_data: core::mem::size_of_val(&v.data) + core::mem::size_of_val(&v.terminator),
399 offset_data: core::mem::offset_of!(StaticString<CAPACITY>, data),
400 size_len: core::mem::size_of_val(&v.len),
401 offset_len: core::mem::offset_of!(StaticString<CAPACITY>, len),
402 len_is_unsigned: v.len.is_unsigned(),
403 }
404 }
405}