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