#![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::strings::string_common::impl_arena_string_common;
use crate::vec::{FromIteratorIn, Vec};
use crate::{Arc, Arena, Box, FromIn};
pub struct String<'a, A: Allocator + Clone = Global> {
pub(super) inner: 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(crate) 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(crate) fn try_from_str_in(s: &str, arena: &'a Arena<A>) -> Result<Self, allocator_api2::alloc::AllocError> {
let mut out = Self::try_with_capacity_in(s.len(), arena)?;
out.try_push_str(s)?;
Ok(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) {
crate::arena::ExpectAlloc::expect_alloc(self.try_insert(idx, ch));
}
pub fn try_insert(&mut self, idx: usize, ch: char) -> Result<(), AllocError> {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
self.try_insert_str(idx, s)
}
pub fn insert_str(&mut self, idx: usize, s: &str) {
crate::arena::ExpectAlloc::expect_alloc(self.try_insert_str(idx, s));
}
pub fn try_insert_str(&mut self, idx: usize, s: &str) -> Result<(), AllocError> {
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 Ok(());
}
self.inner.try_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);
Ok(())
}
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 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) {
crate::arena::ExpectAlloc::expect_alloc(self.try_replace_range(range, replace_with));
}
pub fn try_replace_range<R: RangeBounds<usize>>(&mut self, range: R, replace_with: &str) -> Result<(), AllocError> {
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");
self.inner.try_replace_range_with_slice(start, end, replace_with.as_bytes())
}
#[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_bytes(self) -> Vec<'a, u8, A> {
self.inner
}
#[must_use]
pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<'a, u8, A> {
&mut self.inner
}
#[must_use]
pub fn split_off(&mut self, at: usize) -> Self {
crate::arena::ExpectAlloc::expect_alloc(self.try_split_off(at))
}
pub fn try_split_off(&mut self, at: usize) -> Result<Self, AllocError> {
assert!(self.as_str().is_char_boundary(at), "String::split_off: `at` is not a char boundary");
Ok(Self {
inner: self.inner.try_split_off(at)?,
})
}
pub fn extend_from_within<R: RangeBounds<usize>>(&mut self, src: R) {
crate::arena::ExpectAlloc::expect_alloc(self.try_extend_from_within(src));
}
pub fn try_extend_from_within<R: RangeBounds<usize>>(&mut self, src: R) -> Result<(), AllocError> {
let len = self.len();
let start = match src.start_bound() {
Bound::Included(&i) => i,
Bound::Excluded(&i) => i.checked_add(1).expect("extend_from_within: start bound overflows usize"),
Bound::Unbounded => 0,
};
let end = match src.end_bound() {
Bound::Included(&i) => i.checked_add(1).expect("extend_from_within: end bound overflows usize"),
Bound::Excluded(&i) => i,
Bound::Unbounded => len,
};
assert!(start <= end, "extend_from_within: start > end");
assert!(end <= len, "extend_from_within: end > len");
let s_ref = self.as_str();
assert!(s_ref.is_char_boundary(start), "extend_from_within: start is not on a char boundary");
assert!(s_ref.is_char_boundary(end), "extend_from_within: end is not on a char boundary");
self.inner.try_extend_from_within(start..end)
}
pub fn drain<R: RangeBounds<usize>>(&mut self, range: R) -> Drain<'_, 'a, A> {
let len = self.len();
let start = match range.start_bound() {
Bound::Included(&i) => i,
Bound::Excluded(&i) => i.checked_add(1).expect("drain: start bound overflows usize"),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&i) => i.checked_add(1).expect("drain: end bound overflows usize"),
Bound::Excluded(&i) => i,
Bound::Unbounded => len,
};
assert!(start <= end, "drain: start > end");
assert!(end <= len, "drain: end > len");
let s_ref = self.as_str();
assert!(s_ref.is_char_boundary(start), "drain: start is not on a char boundary");
assert!(s_ref.is_char_boundary(end), "drain: end is not on a char boundary");
Drain {
inner: self.inner.drain(start..end),
}
}
#[must_use]
pub fn into_boxed_str(self) -> Box<str, A> {
crate::arena::ExpectAlloc::expect_alloc(self.try_into_boxed_str())
}
pub fn try_into_boxed_str(self) -> Result<Box<str, A>, AllocError> {
self.inner.arena().try_alloc_str_box(self.as_str())
}
#[must_use]
pub fn into_arc_str(self) -> Arc<str, A>
where
A: Send + Sync,
{
crate::arena::ExpectAlloc::expect_alloc(self.try_into_arc_str())
}
pub fn try_into_arc_str(self) -> Result<Arc<str, A>, AllocError>
where
A: Send + Sync,
{
self.inner.arena().try_alloc_str_arc(self.as_str())
}
#[must_use]
pub fn leak(self) -> &'a mut str {
let bytes = self.inner.leak();
unsafe { str::from_utf8_unchecked_mut(bytes) }
}
}
const fn utf8_seq_len(b0: u8) -> usize {
if b0 < 0x80 {
1
} else if b0 < 0xE0 {
2
} else if b0 < 0xF0 {
3
} else {
4
}
}
pub struct Drain<'d, 'a, A: Allocator + Clone> {
inner: crate::vec::Drain<'d, 'a, u8, A>,
}
impl<A: Allocator + Clone> fmt::Debug for Drain<'_, '_, A> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Drain").finish_non_exhaustive()
}
}
impl<A: Allocator + Clone> Iterator for Drain<'_, '_, A> {
type Item = char;
fn next(&mut self) -> Option<char> {
let b0 = self.inner.next()?;
let len = utf8_seq_len(b0);
let mut buf = [b0, 0, 0, 0];
for slot in buf.iter_mut().take(len).skip(1) {
*slot = self.inner.next().expect("Drain holds valid UTF-8");
}
unsafe { core::str::from_utf8_unchecked(&buf[..len]) }.chars().next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let bytes = self.inner.len();
(bytes.div_ceil(4), Some(bytes))
}
}
impl<A: Allocator + Clone> DoubleEndedIterator for Drain<'_, '_, A> {
fn next_back(&mut self) -> Option<char> {
let last = self.inner.next_back()?;
let mut buf = [0_u8; 4];
buf[3] = last;
let mut n = 1;
let mut b = last;
while b & 0xC0 == 0x80 {
b = self.inner.next_back().expect("Drain holds valid UTF-8");
n += 1;
buf[4 - n] = b;
}
unsafe { core::str::from_utf8_unchecked(&buf[4 - n..]) }.chars().next()
}
}
impl<A: Allocator + Clone> core::iter::FusedIterator for Drain<'_, '_, A> {}
impl<'a, A: Allocator + Clone> From<String<'a, A>> for Box<str, A> {
#[inline]
fn from(s: String<'a, A>) -> Self {
s.into_boxed_str()
}
}
impl<'a, A: Allocator + Clone + Send + Sync> From<String<'a, A>> for Arc<str, A> {
#[inline]
fn from(s: String<'a, A>) -> Self {
s.into_arc_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<String<'_, A>> for str {
#[inline]
fn eq(&self, other: &String<'_, A>) -> bool {
self == other.as_str()
}
}
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<String<'_, A>> for &str {
#[inline]
fn eq(&self, other: &String<'_, A>) -> bool {
*self == other.as_str()
}
}
impl<A: Allocator + Clone> PartialEq<alloc::borrow::Cow<'_, str>> for String<'_, A> {
#[inline]
fn eq(&self, other: &alloc::borrow::Cow<'_, str>) -> bool {
self.as_str() == &**other
}
}
impl<A: Allocator + Clone> PartialEq<String<'_, A>> for alloc::borrow::Cow<'_, str> {
#[inline]
fn eq(&self, other: &String<'_, A>) -> bool {
&**self == other.as_str()
}
}
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> FromIteratorIn<'a, char, A> for String<'a, A> {
fn from_iter_in<I: IntoIterator<Item = char>>(iter: I, arena: &'a Arena<A>) -> Self {
let mut s = Self::new_in(arena);
s.extend(iter);
s
}
}
impl<'a, 'b, A: Allocator + Clone> FromIteratorIn<'a, &'b str, A> for String<'a, A> {
fn from_iter_in<I: IntoIterator<Item = &'b str>>(iter: I, arena: &'a Arena<A>) -> Self {
let mut s = Self::new_in(arena);
s.extend(iter);
s
}
}
impl<'a, 'b, A: Allocator + Clone> FromIteratorIn<'a, &'b char, A> for String<'a, A> {
fn from_iter_in<I: IntoIterator<Item = &'b char>>(iter: I, arena: &'a Arena<A>) -> Self {
let mut s = Self::new_in(arena);
s.extend(iter);
s
}
}
impl<'a, A: Allocator + Clone> FromIteratorIn<'a, alloc::string::String, A> for String<'a, A> {
fn from_iter_in<I: IntoIterator<Item = alloc::string::String>>(iter: I, arena: &'a Arena<A>) -> Self {
let mut s = Self::new_in(arena);
s.extend(iter);
s
}
}
impl<'a, A: Allocator + Clone> FromIteratorIn<'a, alloc::boxed::Box<str>, A> for String<'a, A> {
fn from_iter_in<I: IntoIterator<Item = alloc::boxed::Box<str>>>(iter: I, arena: &'a Arena<A>) -> Self {
let mut s = Self::new_in(arena);
s.extend(iter);
s
}
}
impl<'a, 'b, A: Allocator + Clone> FromIteratorIn<'a, alloc::borrow::Cow<'b, str>, A> for String<'a, A> {
fn from_iter_in<I: IntoIterator<Item = alloc::borrow::Cow<'b, str>>>(iter: I, arena: &'a Arena<A>) -> Self {
let mut s = Self::new_in(arena);
s.extend(iter);
s
}
}
impl<I: core::slice::SliceIndex<str>, A: Allocator + Clone> core::ops::Index<I> for String<'_, A> {
type Output = I::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
core::ops::Index::index(self.as_str(), index)
}
}
impl<I: core::slice::SliceIndex<str>, A: Allocator + Clone> core::ops::IndexMut<I> for String<'_, A> {
#[inline]
fn index_mut(&mut self, index: I) -> &mut Self::Output {
core::ops::IndexMut::index_mut(self.as_mut_str(), index)
}
}
impl<A: Allocator + Clone> AsRef<[u8]> for String<'_, A> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
#[cfg(feature = "std")]
impl<A: Allocator + Clone> AsRef<std::ffi::OsStr> for String<'_, A> {
#[inline]
fn as_ref(&self) -> &std::ffi::OsStr {
self.as_str().as_ref()
}
}
#[cfg(feature = "std")]
impl<A: Allocator + Clone> AsRef<std::path::Path> for String<'_, A> {
#[inline]
fn as_ref(&self) -> &std::path::Path {
self.as_str().as_ref()
}
}
impl<A: Allocator + Clone> core::ops::Add<&str> for String<'_, A> {
type Output = Self;
#[inline]
fn add(mut self, rhs: &str) -> Self {
self.push_str(rhs);
self
}
}
impl<A: Allocator + Clone> core::ops::AddAssign<&str> for String<'_, A> {
#[inline]
fn add_assign(&mut self, rhs: &str) {
self.push_str(rhs);
}
}
impl<'b, A: Allocator + Clone> Extend<&'b char> for String<'_, A> {
fn extend<I: IntoIterator<Item = &'b char>>(&mut self, iter: I) {
for c in iter {
self.push(*c);
}
}
}
impl<'b, A: Allocator + Clone> Extend<alloc::borrow::Cow<'b, str>> for String<'_, A> {
fn extend<I: IntoIterator<Item = alloc::borrow::Cow<'b, str>>>(&mut self, iter: I) {
for s in iter {
self.push_str(&s);
}
}
}
impl<A: Allocator + Clone> Extend<alloc::string::String> for String<'_, A> {
fn extend<I: IntoIterator<Item = alloc::string::String>>(&mut self, iter: I) {
for s in iter {
self.push_str(&s);
}
}
}
impl<A: Allocator + Clone> Extend<alloc::boxed::Box<str>> for String<'_, A> {
fn extend<I: IntoIterator<Item = alloc::boxed::Box<str>>>(&mut self, iter: I) {
for s in iter {
self.push_str(&s);
}
}
}
impl<'a, 'b, A: Allocator + Clone> FromIn<'a, &'b str, A> for String<'a, A> {
#[inline]
fn from_in(value: &'b str, arena: &'a Arena<A>) -> Self {
Self::from_str_in(value, arena)
}
}
impl<'a, 'b, A: Allocator + Clone> FromIn<'a, &'b mut str, A> for String<'a, A> {
#[inline]
fn from_in(value: &'b mut str, arena: &'a Arena<A>) -> Self {
Self::from_str_in(value, arena)
}
}
impl<'a, A: Allocator + Clone> FromIn<'a, char, A> for String<'a, A> {
#[inline]
fn from_in(value: char, arena: &'a Arena<A>) -> Self {
let mut s = Self::new_in(arena);
s.push(value);
s
}
}
impl<'a, 'b, A: Allocator + Clone> FromIn<'a, alloc::borrow::Cow<'b, str>, A> for String<'a, A> {
#[inline]
fn from_in(value: alloc::borrow::Cow<'b, str>, arena: &'a Arena<A>) -> Self {
Self::from_str_in(&value, arena)
}
}
impl<'a, A: Allocator + Clone> FromIn<'a, alloc::boxed::Box<str>, A> for String<'a, A> {
#[inline]
fn from_in(value: alloc::boxed::Box<str>, arena: &'a Arena<A>) -> Self {
Self::from_str_in(&value, arena)
}
}
#[cfg(test)]
mod tests {
use super::utf8_seq_len;
#[test]
fn utf8_seq_len_matches_every_class_boundary() {
assert_eq!(utf8_seq_len(0x00), 1);
assert_eq!(utf8_seq_len(0x7F), 1);
assert_eq!(utf8_seq_len(0x80), 2);
assert_eq!(utf8_seq_len(0xDF), 2);
assert_eq!(utf8_seq_len(0xE0), 3);
assert_eq!(utf8_seq_len(0xEF), 3);
assert_eq!(utf8_seq_len(0xF0), 4);
assert_eq!(utf8_seq_len(0xFF), 4);
}
}