use core::{
alloc::Layout,
borrow::{Borrow, BorrowMut},
ffi::CStr,
fmt::{self, Debug, Display},
hash::Hash,
ops::{Deref, DerefMut, Index, IndexMut, Range, RangeBounds},
ptr::{self, NonNull},
slice::SliceIndex,
str,
};
use crate::{
BumpBox, BumpVec, ErrorBehavior, FixedBumpString, FromUtf8Error, FromUtf16Error,
alloc::AllocError,
destructure::destructure,
fixed_bump_string::RawFixedBumpString,
owned_str,
polyfill::{self, transmute_mut, transmute_value},
traits::{BumpAllocatorTyped, BumpAllocatorTypedScope},
};
#[cfg(feature = "panic-on-alloc")]
use crate::{PanicsOnAlloc, panic_on_error, polyfill::non_null};
#[macro_export]
macro_rules! bump_format {
(in $bump:expr) => {{
$crate::BumpString::new_in($bump)
}};
(in $bump:expr, $($arg:tt)*) => {
$crate::__bump_format_panic_on_alloc!(in $bump, $($arg)*)
};
(try in $bump:expr) => {{
Ok::<_, $crate::alloc::AllocError>($crate::BumpString::new_in($bump))
}};
(try in $bump:expr, $($arg:tt)*) => {{
let mut string = $crate::BumpString::new_in($bump);
match $crate::private::core::fmt::Write::write_fmt(&mut string, $crate::private::core::format_args!($($arg)*)) {
$crate::private::core::result::Result::Ok(_) => $crate::private::core::result::Result::Ok(string),
$crate::private::core::result::Result::Err(_) => $crate::private::core::result::Result::Err($crate::alloc::AllocError),
}
}};
}
#[doc(hidden)]
#[macro_export]
#[cfg(feature = "panic-on-alloc")]
macro_rules! __bump_format_panic_on_alloc {
(in $bump:expr, $($arg:tt)*) => {{
let mut string = $crate::private::PanicsOnAlloc($crate::BumpString::new_in($bump));
match $crate::private::core::fmt::Write::write_fmt(&mut string, $crate::private::core::format_args!($($arg)*)) {
$crate::private::core::result::Result::Ok(_) => string.0,
$crate::private::core::result::Result::Err(_) => $crate::private::format_trait_error(),
}
}};
}
#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "panic-on-alloc"))]
macro_rules! __bump_format_panic_on_alloc {
(in $bump:expr, $($arg:tt)*) => {{
compile_error!(concat!(
"the potentially panicking api of `bump_format!` is not available\n",
"help: enable `bump-scope`'s \"panic-on-alloc\" feature or use `try`:\n",
" `bump_format!(try in ",
stringify!($bump),
", ",
stringify!($($arg)*),
")`"
))
}};
}
#[repr(C)]
pub struct BumpString<A: BumpAllocatorTyped> {
fixed: RawFixedBumpString,
allocator: A,
}
impl<A: BumpAllocatorTyped> BumpString<A> {
#[inline]
pub fn new_in(allocator: A) -> Self {
Self {
fixed: RawFixedBumpString::EMPTY,
allocator,
}
}
pub fn from_utf8(vec: BumpVec<u8, A>) -> Result<Self, FromUtf8Error<BumpVec<u8, A>>> {
match str::from_utf8(vec.as_slice()) {
Ok(_) => Ok(unsafe { transmute_value(vec) }),
Err(error) => Err(FromUtf8Error::new(error, vec)),
}
}
#[must_use]
pub unsafe fn from_utf8_unchecked(vec: BumpVec<u8, A>) -> Self {
unsafe {
debug_assert!(str::from_utf8(vec.as_slice()).is_ok());
transmute_value(vec)
}
}
#[must_use]
#[inline(always)]
pub const fn capacity(&self) -> usize {
self.fixed.capacity()
}
#[must_use]
#[inline(always)]
pub const fn len(&self) -> usize {
self.fixed.len()
}
#[must_use]
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.fixed.len() == 0
}
#[inline(always)]
#[must_use = "`self` will be dropped if the result is not used"]
pub fn into_bytes(self) -> BumpVec<u8, A> {
unsafe { transmute_value(self) }
}
#[track_caller]
#[inline(always)]
pub(crate) fn assert_char_boundary(&self, index: usize) {
unsafe { self.fixed.cook_ref() }.assert_char_boundary(index);
}
#[inline]
#[expect(clippy::return_self_not_must_use)]
pub fn split_off(&mut self, range: impl RangeBounds<usize>) -> Self
where
A: Clone,
{
let other = unsafe { self.fixed.cook_mut() }.split_off(range);
Self {
fixed: unsafe { RawFixedBumpString::from_cooked(other) },
allocator: self.allocator.clone(),
}
}
#[inline]
pub fn pop(&mut self) -> Option<char> {
unsafe { self.fixed.cook_mut() }.pop()
}
#[inline]
pub fn clear(&mut self) {
unsafe { self.fixed.cook_mut() }.clear();
}
#[inline]
pub fn truncate(&mut self, new_len: usize) {
unsafe { self.fixed.cook_mut() }.truncate(new_len);
}
#[inline]
pub fn remove(&mut self, idx: usize) -> char {
unsafe { self.fixed.cook_mut() }.remove(idx)
}
#[inline]
pub fn retain<F>(&mut self, f: F)
where
F: FnMut(char) -> bool,
{
unsafe { self.fixed.cook_mut() }.retain(f);
}
pub fn drain<R>(&mut self, range: R) -> owned_str::Drain<'_>
where
R: RangeBounds<usize>,
{
unsafe { self.fixed.cook_mut() }.drain(range)
}
#[must_use]
#[inline(always)]
pub fn as_str(&self) -> &str {
unsafe { self.fixed.cook_ref() }.as_str()
}
#[must_use]
#[inline(always)]
pub fn as_mut_str(&mut self) -> &mut str {
unsafe { self.fixed.cook_mut() }.as_mut_str()
}
#[must_use]
#[inline(always)]
pub fn as_bytes(&self) -> &[u8] {
unsafe { self.fixed.cook_ref() }.as_bytes()
}
#[must_use]
#[inline(always)]
pub unsafe fn as_mut_vec(&mut self) -> &mut BumpVec<u8, A> {
unsafe { transmute_mut(self) }
}
#[inline]
#[must_use]
pub fn as_ptr(&self) -> *const u8 {
self.fixed.as_ptr()
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.fixed.as_mut_ptr()
}
#[must_use]
#[inline(always)]
pub const fn as_non_null(&self) -> NonNull<u8> {
self.fixed.as_non_null()
}
#[inline(always)]
pub(crate) unsafe fn set_len(&mut self, new_len: usize) {
unsafe { self.fixed.set_len(new_len) };
}
#[must_use]
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn with_capacity_in(capacity: usize, allocator: A) -> Self {
panic_on_error(Self::generic_with_capacity_in(capacity, allocator))
}
#[inline(always)]
pub fn try_with_capacity_in(capacity: usize, allocator: A) -> Result<Self, AllocError> {
Self::generic_with_capacity_in(capacity, allocator)
}
#[inline]
pub(crate) fn generic_with_capacity_in<E: ErrorBehavior>(capacity: usize, allocator: A) -> Result<Self, E> {
if capacity == 0 {
return Ok(Self {
fixed: RawFixedBumpString::EMPTY,
allocator,
});
}
Ok(Self {
fixed: unsafe { RawFixedBumpString::allocate(&allocator, capacity)? },
allocator,
})
}
#[must_use]
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn from_str_in(string: &str, allocator: A) -> Self {
panic_on_error(Self::generic_from_str_in(string, allocator))
}
#[inline(always)]
pub fn try_from_str_in(string: &str, allocator: A) -> Result<Self, AllocError> {
Self::generic_from_str_in(string, allocator)
}
#[inline]
pub(crate) fn generic_from_str_in<E: ErrorBehavior>(string: &str, allocator: A) -> Result<Self, E> {
let mut this = Self::generic_with_capacity_in(string.len(), allocator)?;
unsafe {
ptr::copy_nonoverlapping(string.as_ptr(), this.fixed.as_mut_ptr(), string.len());
this.as_mut_vec().set_len(string.len());
}
Ok(this)
}
#[must_use]
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn from_utf8_lossy_in(v: &[u8], allocator: A) -> Self {
panic_on_error(Self::generic_from_utf8_lossy_in(v, allocator))
}
#[inline(always)]
pub fn try_from_utf8_lossy_in(v: &[u8], allocator: A) -> Result<Self, AllocError> {
Self::generic_from_utf8_lossy_in(v, allocator)
}
pub(crate) fn generic_from_utf8_lossy_in<E: ErrorBehavior>(v: &[u8], allocator: A) -> Result<Self, E> {
let mut iter = v.utf8_chunks();
let first_valid = if let Some(chunk) = iter.next() {
let valid = chunk.valid();
if chunk.invalid().is_empty() {
debug_assert_eq!(valid.len(), v.len());
return Self::generic_from_str_in(valid, allocator);
}
valid
} else {
return Ok(Self::new_in(allocator));
};
const REPLACEMENT: &str = "\u{FFFD}";
let mut res = Self::generic_with_capacity_in(v.len(), allocator)?;
res.generic_push_str(first_valid)?;
res.generic_push_str(REPLACEMENT)?;
for chunk in iter {
res.generic_push_str(chunk.valid())?;
if !chunk.invalid().is_empty() {
res.generic_push_str(REPLACEMENT)?;
}
}
Ok(res)
}
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
#[expect(clippy::missing_errors_doc)]
pub fn from_utf16_in(v: &[u16], allocator: A) -> Result<Self, FromUtf16Error> {
panic_on_error(Self::generic_from_utf16_in(v, allocator))
}
#[inline(always)]
pub fn try_from_utf16_in(v: &[u16], allocator: A) -> Result<Result<Self, FromUtf16Error>, AllocError> {
Self::generic_from_utf16_in(v, allocator)
}
pub(crate) fn generic_from_utf16_in<E: ErrorBehavior>(
v: &[u16],
allocator: A,
) -> Result<Result<Self, FromUtf16Error>, E> {
let mut ret = Self::generic_with_capacity_in(v.len(), allocator)?;
for c in char::decode_utf16(v.iter().copied()) {
if let Ok(c) = c {
ret.generic_push(c)?;
} else {
return Ok(Err(FromUtf16Error(())));
}
}
Ok(Ok(ret))
}
#[must_use]
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn from_utf16_lossy_in(v: &[u16], allocator: A) -> Self {
panic_on_error(Self::generic_from_utf16_lossy_in(v, allocator))
}
#[inline(always)]
pub fn try_from_utf16_lossy_in(v: &[u16], allocator: A) -> Result<Self, AllocError> {
Self::generic_from_utf16_lossy_in(v, allocator)
}
pub(crate) fn generic_from_utf16_lossy_in<E: ErrorBehavior>(v: &[u16], allocator: A) -> Result<Self, E> {
let iter = char::decode_utf16(v.iter().copied());
let capacity = iter.size_hint().0;
let mut string = Self::generic_with_capacity_in(capacity, allocator)?;
for r in iter {
string.generic_push(r.unwrap_or(char::REPLACEMENT_CHARACTER))?;
}
Ok(string)
}
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn push(&mut self, ch: char) {
panic_on_error(self.generic_push(ch));
}
#[inline(always)]
pub fn try_push(&mut self, ch: char) -> Result<(), AllocError> {
self.generic_push(ch)
}
#[inline]
pub(crate) fn generic_push<E: ErrorBehavior>(&mut self, ch: char) -> Result<(), E> {
let vec = unsafe { self.as_mut_vec() };
match ch.len_utf8() {
1 => vec.generic_push(ch as u8),
_ => vec.generic_extend_from_slice_copy(ch.encode_utf8(&mut [0; 4]).as_bytes()),
}
}
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn push_str(&mut self, string: &str) {
panic_on_error(self.generic_push_str(string));
}
#[inline(always)]
pub fn try_push_str(&mut self, string: &str) -> Result<(), AllocError> {
self.generic_push_str(string)
}
#[inline]
pub(crate) fn generic_push_str<E: ErrorBehavior>(&mut self, string: &str) -> Result<(), E> {
let vec = unsafe { self.as_mut_vec() };
vec.generic_extend_from_slice_copy(string.as_bytes())
}
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn insert(&mut self, idx: usize, ch: char) {
panic_on_error(self.generic_insert(idx, ch));
}
#[inline(always)]
pub fn try_insert(&mut self, idx: usize, ch: char) -> Result<(), AllocError> {
self.generic_insert(idx, ch)
}
#[inline]
pub(crate) fn generic_insert<E: ErrorBehavior>(&mut self, idx: usize, ch: char) -> Result<(), E> {
self.assert_char_boundary(idx);
let mut bits = [0; 4];
let bits = ch.encode_utf8(&mut bits).as_bytes();
unsafe { self.insert_bytes(idx, bits) }
}
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn insert_str(&mut self, idx: usize, string: &str) {
panic_on_error(self.generic_insert_str(idx, string));
}
#[inline(always)]
pub fn try_insert_str(&mut self, idx: usize, string: &str) -> Result<(), AllocError> {
self.generic_insert_str(idx, string)
}
#[inline]
pub(crate) fn generic_insert_str<E: ErrorBehavior>(&mut self, idx: usize, string: &str) -> Result<(), E> {
self.assert_char_boundary(idx);
unsafe { self.insert_bytes(idx, string.as_bytes()) }
}
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn extend_from_within<R>(&mut self, src: R)
where
R: RangeBounds<usize>,
{
panic_on_error(self.generic_extend_from_within(src));
}
#[inline(always)]
pub fn try_extend_from_within<R>(&mut self, src: R) -> Result<(), AllocError>
where
R: RangeBounds<usize>,
{
self.generic_extend_from_within(src)
}
#[inline]
pub(crate) fn generic_extend_from_within<E: ErrorBehavior, R: RangeBounds<usize>>(&mut self, src: R) -> Result<(), E> {
let src @ Range { start, end } = polyfill::slice::range(src, ..self.len());
self.assert_char_boundary(start);
self.assert_char_boundary(end);
let vec = unsafe { self.as_mut_vec() };
vec.generic_extend_from_within_copy(src)
}
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn extend_zeroed(&mut self, additional: usize) {
panic_on_error(self.generic_extend_zeroed(additional));
}
#[inline(always)]
pub fn try_extend_zeroed(&mut self, additional: usize) -> Result<(), AllocError> {
self.generic_extend_zeroed(additional)
}
#[inline]
pub(crate) fn generic_extend_zeroed<E: ErrorBehavior>(&mut self, additional: usize) -> Result<(), E> {
let vec = unsafe { self.as_mut_vec() };
vec.generic_reserve(additional)?;
unsafe {
let ptr = vec.as_mut_ptr();
let len = vec.len();
ptr.add(len).write_bytes(0, additional);
vec.set_len(len + additional);
}
Ok(())
}
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn replace_range<R>(&mut self, range: R, replace_with: &str)
where
R: RangeBounds<usize>,
{
panic_on_error(self.generic_replace_range(range, replace_with));
}
#[inline(always)]
pub fn try_replace_range<R>(&mut self, range: R, replace_with: &str) -> Result<(), AllocError>
where
R: RangeBounds<usize>,
{
self.generic_replace_range(range, replace_with)
}
#[inline]
pub(crate) fn generic_replace_range<E: ErrorBehavior, R: RangeBounds<usize>>(
&mut self,
range: R,
replace_with: &str,
) -> Result<(), E> {
let Range { start, end } = polyfill::slice::range(range, ..self.len());
self.assert_char_boundary(start);
self.assert_char_boundary(end);
let range_len = end - start;
let given_len = replace_with.len();
let additional_len = given_len.saturating_sub(range_len);
self.generic_reserve(additional_len)?;
if range_len != given_len {
unsafe {
let src = self.as_ptr().add(end);
let dst = self.as_mut_ptr().add(start + given_len);
let len = self.len() - end;
src.copy_to(dst, len);
}
}
unsafe {
let src = replace_with.as_ptr();
let dst = self.as_mut_ptr().add(start);
let len = replace_with.len();
src.copy_to_nonoverlapping(dst, len);
}
#[expect(clippy::cast_sign_loss, clippy::cast_possible_wrap)]
unsafe {
let len_diff = given_len as isize - range_len as isize;
self.set_len((self.len() as isize + len_diff) as usize);
}
Ok(())
}
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn reserve(&mut self, additional: usize) {
panic_on_error(self.generic_reserve(additional));
}
#[inline(always)]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), AllocError> {
self.generic_reserve(additional)
}
#[inline]
pub(crate) fn generic_reserve<E: ErrorBehavior>(&mut self, additional: usize) -> Result<(), E> {
let vec = unsafe { self.as_mut_vec() };
vec.generic_reserve(additional)
}
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn reserve_exact(&mut self, additional: usize) {
panic_on_error(self.generic_reserve_exact(additional));
}
#[inline(always)]
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), AllocError> {
self.generic_reserve_exact(additional)
}
#[inline]
pub(crate) fn generic_reserve_exact<E: ErrorBehavior>(&mut self, additional: usize) -> Result<(), E> {
let vec = unsafe { self.as_mut_vec() };
vec.generic_reserve_exact(additional)
}
unsafe fn insert_bytes<B: ErrorBehavior>(&mut self, idx: usize, bytes: &[u8]) -> Result<(), B> {
unsafe {
let vec = self.as_mut_vec();
let len = vec.len();
let amt = bytes.len();
vec.generic_reserve(amt)?;
ptr::copy(vec.as_ptr().add(idx), vec.as_mut_ptr().add(idx + amt), len - idx);
ptr::copy_nonoverlapping(bytes.as_ptr(), vec.as_mut_ptr().add(idx), amt);
vec.set_len(len + amt);
Ok(())
}
}
pub fn shrink_to_fit(&mut self) {
let vec = unsafe { self.as_mut_vec() };
vec.shrink_to_fit();
}
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
let vec = unsafe { self.as_mut_vec() };
vec.shrink_to(min_capacity);
}
#[must_use]
#[inline(always)]
pub fn allocator(&self) -> &A {
&self.allocator
}
#[must_use]
#[inline(always)]
pub fn allocator_stats(&self) -> A::TypedStats<'_> {
self.allocator.typed_stats()
}
pub(crate) fn generic_write_fmt<B: ErrorBehavior>(&mut self, args: fmt::Arguments) -> Result<(), B> {
#[cfg(feature = "panic-on-alloc")]
if B::PANICS_ON_ALLOC {
if fmt::Write::write_fmt(PanicsOnAlloc::from_mut(self), args).is_err() {
return Err(B::format_trait_error());
}
return Ok(());
}
if fmt::Write::write_fmt(self, args).is_err() {
return Err(B::format_trait_error());
}
Ok(())
}
}
impl<'a, A: BumpAllocatorTypedScope<'a>> BumpString<A> {
#[must_use]
#[inline(always)]
pub fn into_str(self) -> &'a mut str {
self.into_boxed_str().into_mut()
}
#[must_use]
#[inline(always)]
pub fn into_boxed_str(mut self) -> BumpBox<'a, str> {
self.shrink_to_fit();
self.into_fixed_string().into_boxed_str()
}
#[must_use]
#[inline(always)]
pub fn into_fixed_string(self) -> FixedBumpString<'a> {
self.into_parts().0
}
#[must_use]
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn into_cstr(self) -> &'a CStr {
panic_on_error(self.generic_into_cstr())
}
#[inline(always)]
pub fn try_into_cstr(self) -> Result<&'a CStr, AllocError> {
self.generic_into_cstr()
}
#[inline]
pub(crate) fn generic_into_cstr<B: ErrorBehavior>(mut self) -> Result<&'a CStr, B> {
match self.as_bytes().iter().position(|&c| c == b'\0') {
Some(nul) => unsafe { self.fixed.cook_mut().as_mut_vec().truncate(nul + 1) },
None => self.generic_push('\0')?,
}
let bytes_with_nul = self.into_boxed_str().into_ref().as_bytes();
Ok(unsafe { CStr::from_bytes_with_nul_unchecked(bytes_with_nul) })
}
#[must_use]
#[inline(always)]
pub fn from_parts(string: FixedBumpString<'a>, allocator: A) -> Self {
Self {
fixed: unsafe { RawFixedBumpString::from_cooked(string) },
allocator,
}
}
#[must_use]
#[inline(always)]
pub fn into_parts(self) -> (FixedBumpString<'a>, A) {
destructure!(let Self { fixed, allocator } = self);
(unsafe { fixed.cook() }, allocator)
}
}
impl<A: BumpAllocatorTyped> fmt::Write for BumpString<A> {
#[inline(always)]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.try_push_str(s).map_err(|_| fmt::Error)
}
#[inline(always)]
fn write_char(&mut self, c: char) -> fmt::Result {
self.try_push(c).map_err(|_| fmt::Error)
}
}
#[cfg(feature = "panic-on-alloc")]
impl<A: BumpAllocatorTyped> fmt::Write for PanicsOnAlloc<BumpString<A>> {
#[inline(always)]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0.push_str(s);
Ok(())
}
#[inline(always)]
fn write_char(&mut self, c: char) -> fmt::Result {
self.0.push(c);
Ok(())
}
}
impl<A: BumpAllocatorTyped> Debug for BumpString<A> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
impl<A: BumpAllocatorTyped> Display for BumpString<A> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Display::fmt(self.as_str(), f)
}
}
impl<A: BumpAllocatorTyped> Deref for BumpString<A> {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl<A: BumpAllocatorTyped> DerefMut for BumpString<A> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_str()
}
}
impl<A: BumpAllocatorTyped, I: SliceIndex<str>> Index<I> for BumpString<A> {
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
&self.as_str()[index]
}
}
impl<A: BumpAllocatorTyped, I: SliceIndex<str>> IndexMut<I> for BumpString<A> {
fn index_mut(&mut self, index: I) -> &mut Self::Output {
&mut self.as_mut_str()[index]
}
}
impl<A: BumpAllocatorTyped> Drop for BumpString<A> {
fn drop(&mut self) {
unsafe {
let ptr = self.fixed.as_non_null().cast();
let layout = Layout::from_size_align_unchecked(self.fixed.capacity(), 1);
self.allocator.deallocate(ptr, layout);
}
}
}
impl<A: BumpAllocatorTyped + Default> Default for BumpString<A> {
fn default() -> Self {
Self::new_in(A::default())
}
}
#[cfg(feature = "panic-on-alloc")]
impl<A: BumpAllocatorTyped + Clone> Clone for BumpString<A> {
fn clone(&self) -> Self {
let len = self.len();
let allocator = self.allocator.clone();
let ptr = allocator.allocate_slice::<u8>(len);
unsafe {
self.as_ptr().copy_to_nonoverlapping(ptr.as_ptr(), len);
let slice = non_null::str_from_utf8(NonNull::slice_from_raw_parts(ptr, len));
let fixed = RawFixedBumpString::from_raw_parts(slice, len);
Self { fixed, allocator }
}
}
}
#[cfg(feature = "panic-on-alloc")]
impl<A: BumpAllocatorTyped> core::ops::AddAssign<&str> for BumpString<A> {
#[inline]
fn add_assign(&mut self, rhs: &str) {
self.push_str(rhs);
}
}
impl<A: BumpAllocatorTyped> AsRef<str> for BumpString<A> {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<A: BumpAllocatorTyped> AsMut<str> for BumpString<A> {
#[inline]
fn as_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
impl<A: BumpAllocatorTyped> Borrow<str> for BumpString<A> {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<A: BumpAllocatorTyped> BorrowMut<str> for BumpString<A> {
#[inline]
fn borrow_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
impl<A: BumpAllocatorTyped> Eq for BumpString<A> {}
impl<A: BumpAllocatorTyped> PartialOrd for BumpString<A> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
#[inline]
fn lt(&self, other: &Self) -> bool {
<str as PartialOrd>::lt(self, other)
}
#[inline]
fn le(&self, other: &Self) -> bool {
<str as PartialOrd>::le(self, other)
}
#[inline]
fn gt(&self, other: &Self) -> bool {
<str as PartialOrd>::gt(self, other)
}
#[inline]
fn ge(&self, other: &Self) -> bool {
<str as PartialOrd>::ge(self, other)
}
}
impl<A: BumpAllocatorTyped> Ord for BumpString<A> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
<str as Ord>::cmp(self, other)
}
}
impl<A: BumpAllocatorTyped> Hash for BumpString<A> {
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
#[cfg(feature = "panic-on-alloc")]
impl<'s, A: BumpAllocatorTyped> Extend<&'s str> for BumpString<A> {
#[inline]
fn extend<T: IntoIterator<Item = &'s str>>(&mut self, iter: T) {
for str in iter {
self.push_str(str);
}
}
}
#[cfg(feature = "panic-on-alloc")]
impl<A: BumpAllocatorTyped> Extend<char> for BumpString<A> {
fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
let iterator = iter.into_iter();
let (lower_bound, _) = iterator.size_hint();
self.reserve(lower_bound);
iterator.for_each(move |c| self.push(c));
}
}
#[cfg(feature = "panic-on-alloc")]
impl<'s, A: BumpAllocatorTyped> Extend<&'s char> for BumpString<A> {
fn extend<I: IntoIterator<Item = &'s char>>(&mut self, iter: I) {
self.extend(iter.into_iter().copied());
}
}
#[cfg(feature = "alloc")]
impl<A: BumpAllocatorTyped> From<BumpString<A>> for alloc_crate::string::String {
#[inline]
fn from(value: BumpString<A>) -> Self {
value.as_str().into()
}
}
#[cfg(feature = "panic-on-alloc")]
impl<A: BumpAllocatorTyped> core::ops::Add<&str> for BumpString<A> {
type Output = Self;
#[inline]
fn add(mut self, other: &str) -> Self {
self.push_str(other);
self
}
}