#![no_std]
#![warn(missing_docs)]
#![allow(clippy::style)]
extern crate alloc;
#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "std")]
mod std;
mod core_traits;
mod str_ext;
pub use str_ext::StrExt;
mod utils;
use utils::MiniStr;
mod drain;
pub use drain::Drain;
use core::{ptr, mem};
type HeapStr = minivec::MiniVec<u8>;
const SSO_MAX_SIZE: usize = mem::size_of::<HeapStr>() * 2 - 2;
type StrBuf = str_buf::StrBuf<{SSO_MAX_SIZE}>;
#[inline(always)]
unsafe fn insert_bytes_into(ptr: *mut u8, len: usize, idx: usize, bytes: &[u8]) {
let bytes_len = bytes.len();
ptr::copy(ptr.add(idx), ptr.add(idx + bytes_len), len - idx);
ptr::copy(bytes.as_ptr(), ptr.add(idx), bytes_len);
}
fn assert_range_len(this: &str, start: core::ops::Bound<&usize>, end: core::ops::Bound<&usize>) -> (usize, usize, usize) {
let start = match start {
core::ops::Bound::Included(n) => {
assert!(this.is_char_boundary(*n));
*n
},
core::ops::Bound::Excluded(n) => {
let n = n.saturating_add(1);
assert!(this.is_char_boundary(n));
n
},
core::ops::Bound::Unbounded => 0,
};
let end = match end {
core::ops::Bound::Included(n) => {
let n = n.saturating_add(1);
assert!(this.is_char_boundary(n));
n
},
core::ops::Bound::Excluded(n) => {
assert!(this.is_char_boundary(*n));
*n
},
core::ops::Bound::Unbounded => this.len()
};
if start > end {
panic!("start '{}' is greater than end '{}'", start, end);
}
(start, end, end - start)
}
pub enum String {
#[doc(hidden)]
Heap(HeapStr),
#[doc(hidden)]
Sso(StrBuf),
}
impl String {
#[inline]
pub const fn new() -> Self {
Self::Sso(StrBuf::new())
}
#[inline]
pub fn new_str(text: &str) -> Self {
match StrBuf::from_str_checked(text) {
Ok(sso) => Self::Sso(sso),
Err(_) => Self::Heap(text.into()),
}
}
#[inline]
pub const fn new_sso(text: &str) -> Self {
Self::Sso(StrBuf::from_str(text))
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
if capacity <= StrBuf::capacity() {
Self::new()
} else {
Self::Heap(HeapStr::with_capacity(capacity))
}
}
#[inline]
pub const fn is_alloc(&self) -> bool {
match self {
Self::Heap(_) => true,
Self::Sso(_) => false,
}
}
#[inline]
pub unsafe fn set_len(&mut self, new_len: usize) {
match self {
Self::Heap(ref mut buf) => buf.set_len(new_len ),
Self::Sso(ref mut string) => string.set_len(new_len as u8),
}
}
#[inline(always)]
pub fn len(&self) -> usize {
match self {
Self::Heap(ref buf) => buf.len(),
Self::Sso(ref string) => string.len(),
}
}
#[inline(always)]
fn assert_heap_from_sso(&self, capacity: usize) -> HeapStr {
if let Self::Sso(ref buf) = self {
let mut heap = HeapStr::with_capacity(capacity);
unsafe {
ptr::copy_nonoverlapping(buf.as_ptr() as *const _, heap.as_mut_ptr(), buf.len());
heap.set_len(buf.len());
}
heap
} else {
unreach!()
}
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
let capacity = self.capacity();
let required = self.len() + additional;
if required <= capacity {
return;
}
match self {
Self::Sso(_) => *self = Self::Heap(self.assert_heap_from_sso(required)),
Self::Heap(ref mut string) => string.reserve(additional),
}
}
pub fn reserve_exact(&mut self, additional: usize) {
let capacity = self.capacity();
let required = self.len() + additional;
if required <= capacity {
return;
}
match self {
Self::Sso(_) => *self = Self::Heap(self.assert_heap_from_sso(required)),
Self::Heap(ref mut string) => string.reserve_exact(additional),
}
}
#[inline]
pub fn shrink_to_fit(&mut self) {
if let Self::Heap(ref mut heap) = self {
heap.shrink_to_fit();
}
}
#[inline]
pub fn capacity(&self) -> usize {
match self {
Self::Heap(ref heap) => heap.capacity(),
Self::Sso(_) => StrBuf::capacity(),
}
}
#[inline(always)]
pub fn as_ptr(&self) -> *const u8 {
match self {
Self::Heap(ref heap) => heap.as_ptr(),
Self::Sso(ref sso) => sso.as_ptr(),
}
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
match self {
Self::Heap(ref mut heap) => heap.as_mut_ptr(),
Self::Sso(ref mut sso) => sso.as_mut_ptr(),
}
}
#[inline(always)]
pub fn as_bytes(&self) -> &[u8] {
match self {
Self::Heap(ref heap) => heap.as_slice(),
Self::Sso(ref sso) => sso.as_slice(),
}
}
#[inline(always)]
pub unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
match self {
Self::Heap(ref mut heap) => heap.as_mut_slice(),
Self::Sso(ref mut sso) => sso.as_mut_slice(),
}
}
#[inline(always)]
pub fn as_str(&self) -> &str {
unsafe {
core::str::from_utf8_unchecked(self.as_bytes())
}
}
#[inline(always)]
pub fn as_mut_str(&mut self) -> &mut str {
unsafe {
core::str::from_utf8_unchecked_mut(self.as_mut_bytes())
}
}
#[inline]
pub fn clear(&mut self) {
match self {
Self::Heap(ref mut heap) => heap.clear(),
Self::Sso(ref mut sso) => sso.clear(),
}
}
#[inline]
pub fn truncate(&mut self, new_len: usize) {
match self {
Self::Heap(ref mut heap) => {
if new_len > heap.len() {
return;
}
assert!(heap.as_str().is_char_boundary(new_len));
unsafe {
heap.set_len(new_len);
}
},
Self::Sso(ref mut sso) => {
if new_len > sso.len() {
return;
}
assert!(sso.is_char_boundary(new_len));
unsafe {
sso.set_len(new_len as u8);
}
},
}
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn pop(&mut self) -> Option<char> {
match self {
Self::Heap(ref mut heap) => {
let result = heap.as_str().chars().last()?;
unsafe {
heap.set_len(heap.len() - result.len_utf8());
}
Some(result)
},
Self::Sso(ref mut sso) => {
let result = sso.as_str().chars().last()?;
unsafe {
sso.set_len(sso.len() as u8 - result.len_utf8() as u8);
}
Some(result)
}
}
}
#[inline]
pub fn remove(&mut self, idx: usize) -> char {
match self {
Self::Heap(ref mut heap) => {
let ch = match heap.as_str()[idx..].chars().next() {
Some(ch) => ch,
None => panic!("cannot remove a char from the end of a string")
};
let ptr = heap.as_mut_ptr();
let next = idx + ch.len_utf8();
let len = heap.len();
unsafe {
ptr::copy(ptr.add(next), ptr.add(idx), len - next);
heap.set_len(len - (next - idx));
}
ch
},
Self::Sso(ref mut sso) => {
let ch = match sso.as_str()[idx..].chars().next() {
Some(ch) => ch,
None => panic!("cannot remove a char from the end of a string")
};
let ptr = sso.as_mut_ptr();
let next = idx + ch.len_utf8();
let len = sso.len();
unsafe {
ptr::copy(ptr.add(next), ptr.add(idx), len - next);
sso.set_len(len as u8 - (next as u8 - idx as u8));
}
ch
}
}
}
pub fn retain<F: FnMut(char) -> bool>(&mut self, mut cb: F) {
#[inline(always)]
fn get_char_from_slice(slice: &[u8]) -> Option<char> {
unsafe { core::str::from_utf8_unchecked(slice) }.chars().next()
}
macro_rules! impl_retain {
($storage:expr, $typ:ident) => {
struct LenSetter<'a> {
storage: &'a mut $typ,
idx: usize,
del_bytes: usize,
}
impl<'a> Drop for LenSetter<'a> {
#[inline(always)]
fn drop(&mut self) {
let new_len = self.idx - self.del_bytes;
debug_assert!(new_len <= self.storage.len());
unsafe {
self.storage.set_len(new_len as _);
}
}
}
let mut guard = LenSetter {
storage: $storage,
idx: 0,
del_bytes: 0,
};
let len = guard.storage.len();
while let Some(ch) = guard.storage.as_slice().get(guard.idx..len).and_then(get_char_from_slice) {
let ch_len = ch.len_utf8();
if !cb(ch) {
guard.del_bytes += ch_len;
} else if guard.del_bytes > 0 {
let ptr = guard.storage.as_mut_ptr();
unsafe {
ptr::copy(ptr.add(guard.idx), ptr.add(guard.idx - guard.del_bytes), ch_len);
}
}
guard.idx += ch_len;
}
}
}
match self {
Self::Heap(ref mut heap) => {
impl_retain!(heap, HeapStr);
},
Self::Sso(ref mut sso) => {
impl_retain!(sso, StrBuf);
}
}
}
#[inline(always)]
pub fn push(&mut self, ch: char) {
let mut buf = [0u8; 4];
let res = ch.encode_utf8(&mut buf);
self.push_str(res)
}
#[inline]
pub fn push_str(&mut self, string: &str) {
match self {
Self::Heap(ref mut heap) => heap.extend_from_slice(string.as_bytes()),
Self::Sso(ref mut sso) => {
let len = sso.len();
let string_len = string.len();
if sso.remaining() < string_len {
let mut heap = self.assert_heap_from_sso(len + string_len);
heap.extend_from_slice(string.as_bytes());
*self = Self::Heap(heap);
} else {
unsafe {
sso.push_str_unchecked(string);
}
}
}
}
}
#[inline(always)]
pub fn insert(&mut self, idx: usize, ch: char) {
let mut bits = [0; 4];
self.insert_str(idx, ch.encode_utf8(&mut bits))
}
#[inline]
pub fn insert_str(&mut self, idx: usize, string: &str) {
let string_len = string.len();
match self {
Self::Heap(ref mut heap) => {
assert!(heap.as_str().is_char_boundary(idx));
heap.reserve(string_len);
unsafe {
insert_bytes_into(heap.as_mut_ptr(), heap.len(), idx, string.as_bytes());
heap.set_len(heap.len() + string_len);
}
},
Self::Sso(ref mut sso) => {
assert!(sso.is_char_boundary(idx));
let len = sso.len();
if sso.remaining() < string_len {
let mut heap = self.assert_heap_from_sso(len + string_len);
unsafe {
insert_bytes_into(heap.as_mut_ptr(), heap.len(), idx, string.as_bytes());
heap.set_len(len + string_len);
}
*self = Self::Heap(heap);
} else {
unsafe {
insert_bytes_into(sso.as_mut_ptr(), len, idx, string.as_bytes());
sso.set_len(len as u8 + string_len as u8);
}
}
}
}
}
#[inline]
pub fn drain<R: core::ops::RangeBounds<usize>>(&mut self, range: R) -> Drain<'_> {
let range_start = range.start_bound();
let range_end = range.end_bound();
let (start, end, _) = assert_range_len(self.as_str(), range_start, range_end);
let string = self as *mut _;
let chars = unsafe {
self.as_str().get_unchecked(start..end).chars()
};
Drain {
string,
start,
end,
chars,
}
}
pub fn remove_range<R: core::ops::RangeBounds<usize>>(&mut self, range: R) {
let range_start = range.start_bound();
let range_end = range.end_bound();
match self {
Self::Heap(ref mut heap) => {
let (start, end, range_size) = assert_range_len(heap.as_str(), range_start, range_end);
let ptr = heap.as_mut_ptr();
unsafe {
ptr::copy(ptr.add(end), ptr.add(start), heap.len() - start - range_size);
heap.set_len(heap.len() - range_size);
}
},
Self::Sso(ref mut sso) => {
let (start, end, range_size) = assert_range_len(sso.as_str(), range_start, range_end);
let ptr = sso.as_mut_ptr();
unsafe {
ptr::copy(ptr.add(end), ptr.add(start), sso.len() - start - range_size);
sso.set_len(sso.len() as u8 - range_size as u8);
}
}
}
}
#[inline]
pub fn replace_range<R: core::ops::RangeBounds<usize>>(&mut self, range: R, string: &str) {
let range_start = range.start_bound();
let range_end = range.end_bound();
match self {
Self::Heap(ref mut heap) => {
let text = heap.as_str();
let (start, _, range_size) = assert_range_len(text, range_start, range_end);
if range_size == string.len() {
unsafe {
ptr::copy(string.as_ptr(), heap.as_mut_ptr().add(start), range_size);
}
} else {
heap.splice((range_start, range_end), string.bytes());
}
},
Self::Sso(ref mut sso) => {
let (start, end, range_size) = assert_range_len(sso.as_str(), range_start, range_end);
let ptr = sso.as_mut_ptr();
let required = sso.len() - range_size + string.len();
if StrBuf::capacity() < required {
let mut heap = self.assert_heap_from_sso(required);
heap.splice((range_start, range_end), string.bytes());
*self = Self::Heap(heap);
} else {
if range_size == string.len() {
unsafe {
ptr::copy(string.as_ptr(), sso.as_mut_ptr().add(start), range_size);
}
} else {
if let Some(diff) = range_size.checked_sub(string.len()) {
unsafe {
ptr::copy(ptr.add(end), ptr.add(start + diff), sso.len() - diff);
}
} else {
let diff = string.len() - range_size;
if let Some(len_diff) = sso.len().checked_sub(diff) {
unsafe {
ptr::copy(ptr.add(start + diff), ptr.add(end + diff), len_diff);
}
}
}
unsafe {
ptr::copy(string.as_ptr(), ptr.add(start), string.len());
sso.set_len(required as _);
}
}
}
},
}
}
#[inline]
pub fn from_utf16(utf16: &[u16]) -> Result<Self, core::char::DecodeUtf16Error> {
let mut res = Self::with_capacity(utf16.len());
for ch in char::decode_utf16(utf16.iter().cloned()) {
res.push(ch?);
}
Ok(res)
}
#[inline]
pub fn from_utf16_lossy(utf16: &[u16]) -> Self {
let mut res = Self::with_capacity(utf16.len());
for ch in char::decode_utf16(utf16.iter().cloned()) {
res.push(ch.unwrap_or(core::char::REPLACEMENT_CHARACTER));
}
res
}
}
#[macro_export]
macro_rules! format {
($($arg:tt)*) => {{
let mut res = $crate::String::new();
let _ = core::fmt::Write::write_fmt(&mut res, core::format_args!($($arg)*));
res
}}
}