use alloc::format;
use core::str::FromStr;
use core::{
cmp::Ordering,
fmt::{Debug, Display},
hash::Hash,
mem::MaybeUninit,
ops::{Deref, DerefMut},
};
use iceoryx2_bb_elementary_traits::atomic_copy::AtomicCopy;
use iceoryx2_bb_derive_macros::{PlacementDefault, ZeroCopySend};
use iceoryx2_bb_elementary::math::align_to;
use iceoryx2_bb_elementary_traits::placement_default::PlacementDefault;
use iceoryx2_bb_elementary_traits::zero_copy_send::ZeroCopySend;
use iceoryx2_log::fail;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor};
use crate::string::{
String, StringModificationError, as_escaped_string, internal::StringView, strnlen,
};
#[derive(PlacementDefault, ZeroCopySend, Clone, Copy)]
#[repr(C)]
pub struct StaticString<const CAPACITY: usize> {
data: [MaybeUninit<u8>; CAPACITY],
terminator: u8,
len: u64,
}
unsafe impl<const CAPACITY: usize> AtomicCopy for StaticString<CAPACITY> {
fn __for_each_field<F: FnMut(usize, usize)>(&self, base_offset: usize, callback: &mut F) {
let aligned_base_offset = align_to::<Self>(base_offset);
callback(
aligned_base_offset + core::mem::offset_of!(Self, data),
size_of::<u8>() * self.len(),
);
callback(
aligned_base_offset + core::mem::offset_of!(Self, terminator),
size_of::<u8>(),
);
callback(
aligned_base_offset + core::mem::offset_of!(Self, len),
size_of::<u64>(),
);
}
}
impl<const CAPACITY: usize> Serialize for StaticString<CAPACITY> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(core::str::from_utf8(self.as_bytes()).unwrap())
}
}
struct StaticStringVisitor<const CAPACITY: usize>;
impl<const CAPACITY: usize> Visitor<'_> for StaticStringVisitor<CAPACITY> {
type Value = StaticString<CAPACITY>;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str(&format!("a string with a length of at most {CAPACITY}"))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match StaticString::from_bytes(v.as_bytes()) {
Ok(v) => Ok(v),
Err(_) => Err(E::custom(format!(
"the string exceeds the maximum length of {CAPACITY}"
))),
}
}
}
impl<'de, const CAPACITY: usize> Deserialize<'de> for StaticString<CAPACITY> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(StaticStringVisitor)
}
}
unsafe impl<const CAPACITY: usize> Send for StaticString<CAPACITY> {}
impl<const CAPACITY: usize, const CAPACITY_OTHER: usize> PartialOrd<StaticString<CAPACITY_OTHER>>
for StaticString<CAPACITY>
{
fn partial_cmp(&self, other: &StaticString<CAPACITY_OTHER>) -> Option<Ordering> {
self.as_bytes().partial_cmp(other.as_bytes())
}
}
impl<const CAPACITY: usize> Ord for StaticString<CAPACITY> {
fn cmp(&self, other: &Self) -> Ordering {
self.as_bytes().cmp(other.as_bytes())
}
}
impl<const CAPACITY: usize> Hash for StaticString<CAPACITY> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
state.write(self.as_bytes())
}
}
impl<const CAPACITY: usize> Deref for StaticString<CAPACITY> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_bytes()
}
}
impl<const CAPACITY: usize> DerefMut for StaticString<CAPACITY> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_bytes()
}
}
impl<const CAPACITY: usize, const OTHER_CAPACITY: usize> PartialEq<StaticString<OTHER_CAPACITY>>
for StaticString<CAPACITY>
{
fn eq(&self, other: &StaticString<OTHER_CAPACITY>) -> bool {
*self.as_bytes() == *other.as_bytes()
}
}
impl<const CAPACITY: usize> Eq for StaticString<CAPACITY> {}
impl<const CAPACITY: usize> PartialEq<&[u8]> for StaticString<CAPACITY> {
fn eq(&self, other: &&[u8]) -> bool {
*self.as_bytes() == **other
}
}
impl<const CAPACITY: usize> PartialEq<&str> for StaticString<CAPACITY> {
fn eq(&self, other: &&str) -> bool {
*self.as_bytes() == *other.as_bytes()
}
}
impl<const CAPACITY: usize> PartialEq<StaticString<CAPACITY>> for &str {
fn eq(&self, other: &StaticString<CAPACITY>) -> bool {
*self.as_bytes() == *other.as_bytes()
}
}
impl<const CAPACITY: usize, const OTHER_CAPACITY: usize> PartialEq<[u8; OTHER_CAPACITY]>
for StaticString<CAPACITY>
{
fn eq(&self, other: &[u8; OTHER_CAPACITY]) -> bool {
*self.as_bytes() == *other
}
}
impl<const CAPACITY: usize, const OTHER_CAPACITY: usize> PartialEq<&[u8; OTHER_CAPACITY]>
for StaticString<CAPACITY>
{
fn eq(&self, other: &&[u8; OTHER_CAPACITY]) -> bool {
*self.as_bytes() == **other
}
}
impl<const CAPACITY: usize> Debug for StaticString<CAPACITY> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"StaticString<{}> {{ len: {}, data: \"{}\" }}",
CAPACITY,
self.len,
as_escaped_string(self.as_bytes())
)
}
}
impl<const CAPACITY: usize> Display for StaticString<CAPACITY> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", as_escaped_string(self.as_bytes()))
}
}
impl<const CAPACITY: usize> TryFrom<&str> for StaticString<CAPACITY> {
type Error = StringModificationError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::try_from(value.as_bytes())
}
}
impl<const CAPACITY: usize, const N: usize> TryFrom<&[u8; N]> for StaticString<CAPACITY> {
type Error = StringModificationError;
fn try_from(value: &[u8; N]) -> Result<Self, Self::Error> {
Self::try_from(value.as_slice())
}
}
impl<const CAPACITY: usize> TryFrom<&[u8]> for StaticString<CAPACITY> {
type Error = StringModificationError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if CAPACITY < value.len() {
fail!(from "StaticString::from<&[u8]>()",
with StringModificationError::InsertWouldExceedCapacity,
"The provided string \"{}\" does not fit into the StaticString with capacity {}",
as_escaped_string(value), CAPACITY);
}
let mut new_self = Self::new();
new_self.push_bytes(value)?;
Ok(new_self)
}
}
impl<const CAPACITY: usize> Default for StaticString<CAPACITY> {
fn default() -> Self {
Self::new()
}
}
impl<const CAPACITY: usize> StringView for StaticString<CAPACITY> {
fn data(&self) -> &[MaybeUninit<u8>] {
&self.data
}
unsafe fn data_mut(&mut self) -> &mut [MaybeUninit<u8>] {
&mut self.data
}
unsafe fn set_len(&mut self, len: u64) {
self.len = len
}
}
impl<const CAPACITY: usize> FromStr for StaticString<CAPACITY> {
type Err = StringModificationError;
fn from_str(s: &str) -> Result<Self, StringModificationError> {
Self::from_bytes(s.as_bytes())
}
}
impl<const CAPACITY: usize> StaticString<CAPACITY> {
pub const fn new() -> Self {
let mut new_self = Self {
len: 0,
data: unsafe { MaybeUninit::uninit().assume_init() },
terminator: 0,
};
new_self.data[0] = MaybeUninit::new(0);
new_self
}
pub const unsafe fn from_bytes_unchecked_restricted(bytes: &[u8], len: usize) -> Self {
debug_assert!(bytes.len() <= CAPACITY);
debug_assert!(len <= bytes.len());
let mut new_self = Self::new();
unsafe {
core::ptr::copy_nonoverlapping(bytes.as_ptr(), new_self.data.as_mut_ptr().cast(), len);
core::ptr::write::<u8>(new_self.data.as_mut_ptr().add(len).cast(), 0);
}
new_self.len = len as u64;
new_self
}
pub const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
debug_assert!(bytes.len() <= CAPACITY);
unsafe { Self::from_bytes_unchecked_restricted(bytes, bytes.len()) }
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, StringModificationError> {
let mut new_self = Self::new();
new_self.insert_bytes(0, bytes)?;
Ok(new_self)
}
pub fn from_bytes_truncated(bytes: &[u8]) -> Result<Self, StringModificationError> {
let mut new_self = Self::new();
new_self.insert_bytes(0, &bytes[0..core::cmp::min(bytes.len(), CAPACITY)])?;
Ok(new_self)
}
pub fn from_str_truncated(s: &str) -> Result<Self, StringModificationError> {
Self::from_bytes_truncated(s.as_bytes())
}
pub unsafe fn from_c_str(
ptr: *const core::ffi::c_char,
) -> Result<Self, StringModificationError> {
let string_length = unsafe { strnlen(ptr, CAPACITY + 1) };
if CAPACITY < string_length {
return Err(StringModificationError::InsertWouldExceedCapacity);
}
Self::from_bytes(unsafe { core::slice::from_raw_parts(ptr.cast(), string_length) })
}
pub const fn capacity() -> usize {
CAPACITY
}
pub const fn as_bytes_const(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), self.len as usize) }
}
}
impl<const CAPACITY: usize> String for StaticString<CAPACITY> {
fn capacity(&self) -> usize {
CAPACITY
}
fn len(&self) -> usize {
self.len as usize
}
}
#[allow(missing_docs)]
pub struct StringMemoryLayoutMetrics {
pub string_size: usize,
pub string_alignment: usize,
pub size_data: usize,
pub offset_data: usize,
pub size_len: usize,
pub offset_len: usize,
pub len_is_unsigned: bool,
}
trait _StringMemoryLayoutFieldLenInspection {
fn is_unsigned(&self) -> bool;
}
impl _StringMemoryLayoutFieldLenInspection for u64 {
fn is_unsigned(&self) -> bool {
true
}
}
impl StringMemoryLayoutMetrics {
#[allow(missing_docs)]
pub fn from_string<const CAPACITY: usize>(v: &StaticString<CAPACITY>) -> Self {
StringMemoryLayoutMetrics {
string_size: core::mem::size_of_val(v),
string_alignment: core::mem::align_of_val(v),
size_data: core::mem::size_of_val(&v.data) + core::mem::size_of_val(&v.terminator),
offset_data: core::mem::offset_of!(StaticString<CAPACITY>, data),
size_len: core::mem::size_of_val(&v.len),
offset_len: core::mem::offset_of!(StaticString<CAPACITY>, len),
len_is_unsigned: v.len.is_unsigned(),
}
}
}