Skip to content

Commit

Permalink
feat: added (Mut)BumpString's (try_)into_cstr
Browse files Browse the repository at this point in the history
  • Loading branch information
bluurryy committed Nov 21, 2024
1 parent 45f4f57 commit 31d52bc
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased
- **added:** implemented `NoDrop` for `CStr`, `OsStr` and `Path`
- **added:** `alloc_cstr`, `alloc_cstr_from_str`, `alloc_cstr_fmt` and `alloc_cstr_fmt_mut`
- **added:** `(Mut)BumpString`'s `(try_)into_cstr`

## 0.12.0 (2024-11-17)
- **breaking:** redesigned `OwnedSlice` trait
Expand Down
8 changes: 8 additions & 0 deletions crates/test-fallibility/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ up_and_down! {
MutBumpString::try_with_capacity_in(capacity, bump)
}

pub fn MutBumpString_try_into_cstr(string: MutBumpString) -> Result<&CStr> {
string.try_into_cstr()
}

pub fn BumpVec_try_extend_from_array(vec: &mut BumpVec<u32>, array: [u32; 24]) -> Result {
vec.try_extend_from_array(array)
}
Expand Down Expand Up @@ -433,6 +437,10 @@ up_and_down! {
BumpString::try_with_capacity_in(capacity, bump)
}

pub fn BumpString_try_into_cstr(string: BumpString) -> Result<&CStr> {
string.try_into_cstr()
}

pub fn FixedBumpVec__new(capacity: usize, bump: &Bump) -> Result<FixedBumpVec<u32>> {
bump.try_alloc_fixed_vec(capacity)
}
Expand Down
14 changes: 4 additions & 10 deletions src/bump_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,7 @@ where

let mut string = BumpString::new_in(self);

let mut string = if B::IS_FALLIBLE {
let string = if B::IS_FALLIBLE {
if fmt::Write::write_fmt(&mut string, args).is_err() {
// Either the allocation failed or the formatting trait
// implementation returned an error.
Expand Down Expand Up @@ -903,10 +903,7 @@ where
}
};

string.generic_push('\0')?;
let bytes = string.into_boxed_str().into_ref().as_bytes();

Ok(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) })
string.generic_into_cstr()
}

#[inline(always)]
Expand All @@ -917,7 +914,7 @@ where

let mut string = MutBumpString::generic_with_capacity_in(0, self)?;

let mut string = if B::IS_FALLIBLE {
let string = if B::IS_FALLIBLE {
if fmt::Write::write_fmt(&mut string, args).is_err() {
// Either the allocation failed or the formatting trait
// implementation returned an error.
Expand Down Expand Up @@ -946,10 +943,7 @@ where
}
};

string.generic_push('\0')?;
let bytes = string.into_boxed_str().into_ref().as_bytes();

Ok(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) })
string.generic_into_cstr()
}

#[inline(always)]
Expand Down
59 changes: 58 additions & 1 deletion src/bump_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@ use crate::{
use core::{
alloc::Layout,
borrow::{Borrow, BorrowMut},
ffi::CStr,
fmt::{self, Debug, Display},
hash::Hash,
ops::{Deref, DerefMut, Range, RangeBounds},
panic::{RefUnwindSafe, UnwindSafe},
ptr, str,
};

use allocator_api2::alloc::AllocError;

#[cfg(feature = "panic-on-alloc")]
use core::mem::MaybeUninit;

#[cfg(feature = "panic-on-alloc")]
use crate::{polyfill::nonnull, raw_bump_box::RawBumpBox};
use crate::{infallible, polyfill::nonnull, raw_bump_box::RawBumpBox};

/// This is like [`format!`] but allocates inside a bump allocator, returning a [`BumpString`].
///
Expand Down Expand Up @@ -1307,6 +1310,60 @@ impl<'a, A: BumpAllocatorScope<'a>> BumpString<A> {
self.into_parts().0
}

/// Converts this `BumpString` into `&CStr` that is live for this bump scope.
///
/// # Panics
/// Panics if the allocation fails.
///
/// # Examples
/// ```
/// # use bump_scope::{ Bump, BumpString };
/// # let bump: Bump = Bump::new();
/// let mut hello = BumpString::from_str_in("Hello, ", &bump);
///
/// hello.push('w');
/// hello.push_str("orld!");
///
/// assert_eq!(hello.into_cstr(), c"Hello, world!");
/// ```
#[must_use]
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn into_cstr(self) -> &'a CStr {
infallible(self.generic_into_cstr())
}

/// Converts this `BumpString` into `&CStr` that is live for this bump scope.
///
/// # Errors
/// Errors if the allocation fails.
///
/// # Examples
/// ```
/// # #![cfg_attr(feature = "nightly-allocator-api", feature(allocator_api))]
/// # use bump_scope::{ Bump, BumpString };
/// # let bump: Bump = Bump::try_new()?;
/// let mut hello = BumpString::from_str_in("Hello, ", &bump);
///
/// hello.push('w');
/// hello.push_str("orld!");
///
/// assert_eq!(hello.into_cstr(), c"Hello, world!");
///
/// # Ok::<(), bump_scope::allocator_api2::alloc::AllocError>(())
/// ```
#[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> {
self.generic_push('\0')?;
let bytes = self.into_boxed_str().into_ref().as_bytes();
Ok(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) })
}

/// Creates a `BumpString` from its parts.
///
/// The provided `bump` does not have to be the one the `fixed_string` was allocated in.
Expand Down
59 changes: 58 additions & 1 deletion src/mut_bump_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ use crate::{
};
use core::{
borrow::{Borrow, BorrowMut},
ffi::CStr,
fmt::{self, Debug, Display},
hash::Hash,
ops::{Deref, DerefMut, Range, RangeBounds},
panic::{RefUnwindSafe, UnwindSafe},
ptr, str,
};

use allocator_api2::alloc::AllocError;

#[cfg(feature = "panic-on-alloc")]
use crate::Infallibly;
use crate::{infallible, Infallibly};

/// This is like [`format!`] but allocates inside a *mutable* `Bump` or `BumpScope`, returning a [`MutBumpString`].
///
Expand Down Expand Up @@ -1244,6 +1247,60 @@ impl<'a, A: MutBumpAllocatorScope<'a>> MutBumpString<A> {
let bytes = self.into_bytes().into_boxed_slice();
unsafe { BumpBox::from_utf8_unchecked(bytes) }
}

/// Converts this `MutBumpString` into `&CStr` that is live for this bump scope.
///
/// # Panics
/// Panics if the allocation fails.
///
/// # Examples
/// ```
/// # use bump_scope::{ Bump, MutBumpString };
/// # let mut bump: Bump = Bump::new();
/// let mut hello = MutBumpString::from_str_in("Hello, ", &mut bump);
///
/// hello.push('w');
/// hello.push_str("orld!");
///
/// assert_eq!(hello.into_cstr(), c"Hello, world!");
/// ```
#[must_use]
#[inline(always)]
#[cfg(feature = "panic-on-alloc")]
pub fn into_cstr(self) -> &'a CStr {
infallible(self.generic_into_cstr())
}

/// Converts this `BumpString` into `&CStr` that is live for this bump scope.
///
/// # Errors
/// Errors if the allocation fails.
///
/// # Examples
/// ```
/// # #![cfg_attr(feature = "nightly-allocator-api", feature(allocator_api))]
/// # use bump_scope::{ Bump, MutBumpString };
/// # let mut bump: Bump = Bump::try_new()?;
/// let mut hello = MutBumpString::from_str_in("Hello, ", &mut bump);
///
/// hello.push('w');
/// hello.push_str("orld!");
///
/// assert_eq!(hello.into_cstr(), c"Hello, world!");
///
/// # Ok::<(), bump_scope::allocator_api2::alloc::AllocError>(())
/// ```
#[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> {
self.generic_push('\0')?;
let bytes = self.into_boxed_str().into_ref().as_bytes();
Ok(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) })
}
}

impl<A: MutBumpAllocator> fmt::Write for MutBumpString<A> {
Expand Down

0 comments on commit 31d52bc

Please sign in to comment.