#![allow(
clippy::missing_panics_doc,
clippy::undocumented_unsafe_blocks,
reason = "reachable panics are programmer error, and unsafe code follows the documented `data`/`len`/`cap` invariants"
)]
use core::borrow::{Borrow, BorrowMut};
use core::cmp::Ordering;
use core::fmt::{self, Debug, Display, Formatter};
use core::hash::{Hash, Hasher};
use core::ops::{Bound, Deref, DerefMut, RangeBounds};
use core::str;
use allocator_api2::alloc::{AllocError, Allocator, Global};
use crate::Arena;
use crate::strings::string_common::impl_arena_string_common;
pub struct String<'a, A: Allocator + Clone = Global> {
pub(super) inner: crate::vec::Vec<'a, u8, A>,
}
impl<'a, A: Allocator + Clone> String<'a, A> {
#[must_use]
pub fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.inner.as_slice()) }
}
#[must_use]
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_slice()
}
#[inline]
pub fn as_mut_str(&mut self) -> &mut str {
unsafe { str::from_utf8_unchecked_mut(self.inner.as_mut_slice()) }
}
#[must_use]
pub fn from_str_in(s: &str, arena: &'a Arena<A>) -> Self {
let mut out = Self::with_capacity_in(s.len(), arena);
out.push_str(s);
out
}
pub fn pop(&mut self) -> Option<char> {
let ch = self.as_str().chars().next_back()?;
let new_len = self.len() - ch.len_utf8();
self.inner.truncate(new_len);
Some(ch)
}
pub fn truncate(&mut self, new_len: usize) {
if new_len >= self.len() {
return;
}
assert!(
self.as_str().is_char_boundary(new_len),
"truncate: new_len is not on a char boundary"
);
self.inner.truncate(new_len);
}
pub fn insert(&mut self, idx: usize, ch: char) {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
self.insert_str(idx, s);
}
pub fn insert_str(&mut self, idx: usize, s: &str) {
let len = self.inner.len();
assert!(idx <= len, "insertion index out of bounds (was {idx}, len = {len})");
assert!(self.as_str().is_char_boundary(idx), "insert_str: idx is not on a char boundary");
let bytes = s.as_bytes();
let added = bytes.len();
if added == 0 {
return;
}
self.inner.reserve(added);
for &b in bytes {
self.inner.push(b);
}
let region = &mut self.inner.as_mut_slice()[idx..len + added];
region.rotate_right(added);
}
pub fn remove(&mut self, idx: usize) -> char {
let ch = self.as_str()[idx..].chars().next().expect("remove: idx out of bounds");
let ch_len = ch.len_utf8();
let len = self.inner.len();
let region = &mut self.inner.as_mut_slice()[idx..len];
region.rotate_left(ch_len);
self.inner.truncate(len - ch_len);
ch
}
#[cfg_attr(test, mutants::skip)] pub fn retain<F: FnMut(char) -> bool>(&mut self, mut f: F) {
struct Guard<'g, 'a, A: Allocator + Clone> {
inner: &'g mut crate::vec::Vec<'a, u8, A>,
idx: usize,
del_bytes: usize,
}
impl<A: Allocator + Clone> Drop for Guard<'_, '_, A> {
fn drop(&mut self) {
self.inner.truncate(self.idx - self.del_bytes);
}
}
let len = self.inner.len();
let mut guard = Guard {
inner: &mut self.inner,
idx: 0,
del_bytes: 0,
};
while guard.idx < len {
let ch = unsafe { str::from_utf8_unchecked(&guard.inner.as_slice()[guard.idx..len]) }
.chars()
.next()
.expect("idx < len guarantees a remaining char");
let ch_len = ch.len_utf8();
if f(ch) {
if guard.del_bytes > 0 {
let dst = guard.idx - guard.del_bytes;
guard.inner.as_mut_slice().copy_within(guard.idx..guard.idx + ch_len, dst);
}
} else {
guard.del_bytes += ch_len;
}
guard.idx += ch_len;
}
drop(guard);
}
pub fn replace_range<R: RangeBounds<usize>>(&mut self, range: R, replace_with: &str) {
let len = self.len();
let start = match range.start_bound() {
Bound::Included(&i) => i,
Bound::Excluded(&i) => i.checked_add(1).expect("replace_range: start bound overflows usize"),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&i) => i.checked_add(1).expect("replace_range: end bound overflows usize"),
Bound::Excluded(&i) => i,
Bound::Unbounded => len,
};
assert!(start <= end, "replace_range: start > end");
assert!(end <= len, "replace_range: end > len");
let s_ref = self.as_str();
assert!(s_ref.is_char_boundary(start), "replace_range: start is not on a char boundary");
assert!(s_ref.is_char_boundary(end), "replace_range: end is not on a char boundary");
let mut staging: allocator_api2::vec::Vec<u8> = allocator_api2::vec::Vec::with_capacity(start + replace_with.len() + (len - end));
staging.extend_from_slice(&self.as_bytes()[..start]);
staging.extend_from_slice(replace_with.as_bytes());
staging.extend_from_slice(&self.as_bytes()[end..]);
self.inner.clear();
self.inner.extend_from_slice(staging.as_slice());
}
#[inline]
pub fn push(&mut self, ch: char) {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
self.push_str(&*s);
}
#[inline]
pub fn try_push(&mut self, ch: char) -> Result<(), AllocError> {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
self.try_push_str(&*s)
}
#[inline]
pub fn push_str(&mut self, s: impl AsRef<str>) {
self.inner.extend_from_slice(s.as_ref().as_bytes());
}
#[inline(always)]
#[allow(
clippy::inline_always,
reason = "the hot path through `try_push_str` is the bump-then-memcpy fast path; the cold grow branch is `#[inline(never)]` so the inlinable body is small"
)]
pub fn try_push_str(&mut self, s: impl AsRef<str>) -> Result<(), AllocError> {
self.inner.try_extend_from_slice(s.as_ref().as_bytes())
}
#[must_use]
pub fn into_arena_box_str(self) -> crate::Box<str, A> {
self.inner.arena().alloc_str_box(self.as_str())
}
}
impl<A: Allocator + Clone> Deref for String<'_, A> {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
impl<A: Allocator + Clone> DerefMut for String<'_, A> {
#[inline]
fn deref_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
impl<A: Allocator + Clone> AsRef<str> for String<'_, A> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<A: Allocator + Clone> AsMut<str> for String<'_, A> {
fn as_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
impl<A: Allocator + Clone> Borrow<str> for String<'_, A> {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<A: Allocator + Clone> BorrowMut<str> for String<'_, A> {
fn borrow_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
impl<A: Allocator + Clone> Debug for String<'_, A> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
impl<A: Allocator + Clone> Display for String<'_, A> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(self.as_str(), f)
}
}
impl<A: Allocator + Clone> PartialEq for String<'_, A> {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl_arena_string_common!(String, u8);
impl<A: Allocator + Clone> Ord for String<'_, A> {
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl<A: Allocator + Clone> Hash for String<'_, A> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
impl<A: Allocator + Clone> PartialEq<str> for String<'_, A> {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl<A: Allocator + Clone> PartialEq<&str> for String<'_, A> {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl<A: Allocator + Clone> Clone for String<'_, A> {
fn clone(&self) -> Self {
Self::from_str_in(self.as_str(), self.inner.arena)
}
}
impl<A: Allocator + Clone> Extend<char> for String<'_, A> {
fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
let iter = iter.into_iter();
let (lo, _) = iter.size_hint();
self.reserve(lo);
for ch in iter {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
self.push_str(s);
}
}
}
impl<'a, A: Allocator + Clone> Extend<&'a str> for String<'_, A> {
fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
for s in iter {
self.push_str(s);
}
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<A: Allocator + Clone> serde::ser::Serialize for String<'_, A> {
fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'a, A: Allocator + Clone> crate::vec::FromIteratorIn<char> for String<'a, A> {
type Allocator = &'a Arena<A>;
fn from_iter_in<I: IntoIterator<Item = char>>(iter: I, allocator: &'a Arena<A>) -> Self {
let mut s = Self::new_in(allocator);
s.extend(iter);
s
}
}
impl<'a, 'b, A: Allocator + Clone> crate::vec::FromIteratorIn<&'b str> for String<'a, A> {
type Allocator = &'a Arena<A>;
fn from_iter_in<I: IntoIterator<Item = &'b str>>(iter: I, allocator: &'a Arena<A>) -> Self {
let mut s = Self::new_in(allocator);
s.extend(iter);
s
}
}