use core::mem::MaybeUninit;
use core::{
fmt::Debug,
fmt::Display,
hash::Hash,
ops::{Deref, DerefMut},
};
use iceoryx2_log::{fail, fatal_panic};
pub mod polymorphic_string;
pub mod static_string;
pub mod relocatable_string;
pub mod utils;
pub use polymorphic_string::*;
pub use relocatable_string::*;
pub use static_string::*;
pub use utils::*;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum StringModificationError {
InvalidCharacter,
InsertWouldExceedCapacity,
}
impl core::fmt::Display for StringModificationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "StringModificationError::{self:?}")
}
}
impl core::error::Error for StringModificationError {}
#[doc(hidden)]
pub(crate) mod internal {
use super::*;
#[doc(hidden)]
pub trait StringView {
fn data(&self) -> &[MaybeUninit<u8>];
unsafe fn data_mut(&mut self) -> &mut [MaybeUninit<u8>];
unsafe fn set_len(&mut self, len: u64);
}
}
pub trait String:
internal::StringView
+ Debug
+ Display
+ PartialOrd
+ Ord
+ Hash
+ Deref<Target = [u8]>
+ DerefMut
+ PartialEq
+ Eq
{
fn as_bytes(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.data().as_ptr() as *const u8, self.len()) }
}
fn as_bytes_with_nul(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.data().as_ptr() as *const u8, self.len() + 1) }
}
fn as_c_str(&self) -> *const core::ffi::c_char {
self.data().as_ptr() as *const core::ffi::c_char
}
fn as_mut_bytes(&mut self) -> &mut [u8] {
unsafe {
core::slice::from_raw_parts_mut(self.data_mut().as_mut_ptr() as *mut u8, self.len())
}
}
fn as_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
}
fn capacity(&self) -> usize;
fn clear(&mut self) {
unsafe { self.set_len(0) };
unsafe { self.data_mut()[0].write(0) };
}
fn find(&self, bytes: &[u8]) -> Option<usize> {
if self.len() < bytes.len() {
return None;
}
for i in 0..self.len() - bytes.len() + 1 {
let mut has_found = true;
for (n, byte) in bytes.iter().enumerate() {
if unsafe { *self.data()[i + n].as_ptr() } != *byte {
has_found = false;
break;
}
}
if has_found {
return Some(i);
}
}
None
}
fn is_empty(&self) -> bool {
self.len() == 0
}
fn is_full(&self) -> bool {
self.len() == self.capacity()
}
fn insert(&mut self, idx: usize, byte: u8) -> Result<(), StringModificationError> {
self.insert_bytes(idx, &[byte; 1])
}
fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) -> Result<(), StringModificationError> {
let msg = "Unable to insert byte string";
if self.len() < idx {
fatal_panic!(from self, "{} \"{}\" since the index {} is out of bounds.",
msg, as_escaped_string(bytes) , idx);
}
if self.capacity() < self.len() + bytes.len() {
fail!(from self, with StringModificationError::InsertWouldExceedCapacity,
"{} \"{}\" since it would exceed the maximum capacity of {}.",
msg, as_escaped_string(bytes), self.capacity());
}
for byte in bytes {
if 128 <= *byte || 0 == *byte {
fail!(from self, with StringModificationError::InvalidCharacter,
"{} \"{}\" since it contains unsupported unicode points. Only unicode points less than 128 (U+0080) are supported",
msg, as_escaped_string(bytes));
}
}
unsafe { self.insert_bytes_unchecked(idx, bytes) };
Ok(())
}
unsafe fn insert_bytes_unchecked(&mut self, idx: usize, bytes: &[u8]) {
unsafe {
let data = self.data_mut();
let ptr = data.as_mut_ptr();
core::ptr::copy(ptr.add(idx), ptr.add(idx + bytes.len()), self.len() - idx);
for (i, byte) in bytes.iter().enumerate() {
self.data_mut()[idx + i].write(*byte);
}
let new_len = self.len() + bytes.len();
self.set_len(new_len as u64);
if new_len < self.capacity() {
self.data_mut()[new_len].write(0);
}
}
}
fn len(&self) -> usize;
fn pop(&mut self) -> Option<u8> {
if self.is_empty() {
return None;
}
self.remove(self.len() - 1)
}
fn push(&mut self, byte: u8) -> Result<(), StringModificationError> {
self.insert(self.len(), byte)
}
fn push_bytes(&mut self, bytes: &[u8]) -> Result<(), StringModificationError> {
self.insert_bytes(self.len(), bytes)
}
fn remove(&mut self, idx: usize) -> Option<u8> {
if self.len() < idx {
return None;
}
let removed_byte = unsafe { *self.data()[idx].as_ptr() };
self.remove_range(idx, 1);
Some(removed_byte)
}
fn remove_range(&mut self, idx: usize, len: usize) -> bool {
if self.len() < idx + len {
return false;
}
if self.len() != idx + len {
let data = unsafe { self.data_mut() };
let ptr = data.as_mut_ptr();
unsafe {
core::ptr::copy(ptr.add(idx + len), ptr.add(idx), self.len() - (idx + len));
}
}
let new_len = self.len() - len;
unsafe { self.data_mut()[new_len].write(0) };
unsafe { self.set_len(new_len as u64) };
true
}
fn retain<F: FnMut(u8) -> bool>(&mut self, mut f: F) {
let len = self.len();
for idx in (0..len).rev() {
if f(unsafe { *self.data()[idx].as_ptr() }) {
self.remove(idx);
}
}
}
fn rfind(&self, bytes: &[u8]) -> Option<usize> {
if self.len() < bytes.len() {
return None;
}
for i in (0..self.len() - bytes.len() + 1).rev() {
let mut has_found = true;
for (n, byte) in bytes.iter().enumerate() {
if unsafe { *self.data()[i + n].as_ptr() } != *byte {
has_found = false;
break;
}
}
if has_found {
return Some(i);
}
}
None
}
fn strip_prefix(&mut self, bytes: &[u8]) -> bool {
match self.find(bytes) {
Some(0) => {
self.remove_range(0, bytes.len());
true
}
_ => false,
}
}
fn strip_suffix(&mut self, bytes: &[u8]) -> bool {
if self.len() < bytes.len() {
return false;
}
let pos = self.len() - bytes.len();
match self.rfind(bytes) {
Some(v) => {
if v != pos {
return false;
}
self.remove_range(pos, bytes.len())
}
None => false,
}
}
fn truncate(&mut self, new_len: usize) {
if self.len() < new_len {
return;
}
if new_len < self.capacity() {
unsafe { self.data_mut()[new_len].write(0u8) };
}
unsafe { self.set_len(new_len as u64) };
}
}