Skip to content

Commit

Permalink
perf: inline and tweak ThinStr* internals
Browse files Browse the repository at this point in the history
  • Loading branch information
morrisonlevi committed Dec 24, 2024
1 parent 0cf60e9 commit 8bafea5
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 20 deletions.
4 changes: 1 addition & 3 deletions thin-str/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,8 @@ impl<const N: usize> ConstStorage<N> {
// ConstStorage only holds valid strs, and the lifetime is valid.
unsafe { Storage::from_header(header_ptr) }
}
}

impl<const N: usize> AsRef<str> for ConstStorage<N> {
fn as_ref(&self) -> &str {
pub const fn as_ref(&self) -> &str {
// SAFETY: ConstStorage always hold valid strings.
unsafe { core::str::from_utf8_unchecked(&self.data) }
}
Expand Down
8 changes: 0 additions & 8 deletions thin-str/src/thin_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,6 @@ impl<'a> From<&ThinStr<'a>> for &'a Storage {
}
}

impl<'a> From<&'a Storage> for ThinStr<'a> {
fn from(storage: &'a Storage) -> ThinStr<'a> {
let obj = ptr::NonNull::from(storage).cast::<ThinHeader>();
// SAFETY: pointer to Storage conforms to layout required for ThinStr.
unsafe { ThinStr::from_raw(obj) }
}
}

impl Deref for ThinStr<'_> {
type Target = str;

Expand Down
32 changes: 23 additions & 9 deletions thin-str/src/thin_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ unsafe impl<A: Allocator + Sync> Sync for ThinString<A> {}
impl<A: Allocator> ThinString<A> {
/// Creates the empty ThinString.
/// This will not allocate.
#[inline]
pub fn new_in(allocator: A) -> Self {
Self::from_const_storage_in(&EMPTY, allocator)
}

/// Create a ThinString from &static [ConstStorage]. Note that there's not
/// _quite_ enough support to make this a const fn.
/// This will not allocate.
#[inline]
pub fn from_const_storage_in<const N: usize>(
const_storage: &'static ConstStorage<N>,
allocator: A,
Expand All @@ -64,6 +66,7 @@ impl<A: Allocator> ThinString<A> {
/// This will allocate in the given allocator.
/// # Panics
/// This panics if allocation fails.
#[inline]
pub fn from_str_in(string: &str, allocator: A) -> Self {
Self::try_from_str_in(string, allocator).unwrap()
}
Expand All @@ -73,16 +76,16 @@ impl<A: Allocator> ThinString<A> {
pub fn try_from_str_in(string: &str, allocator: A) -> Result<Self, AllocError> {
let header = Layout::new::<ThinHeader>();
let data = Layout::for_value(string);
let Ok((layout, offset)) = header.extend(data) else {
let Ok((layout, _offset)) = header.extend(data) else {
return Err(AllocError);
};
// Padding is important here, since the str could be an odd number of
// bytes, and `Layout::extend` doesn't automatically pad.
let layout = layout.pad_to_align();

// Sanity check some things to defend against refactoring.
debug_assert_eq!(offset, mem::size_of::<ThinHeader>());
debug_assert_eq!(offset, mem::size_of::<usize>());
debug_assert_eq!(_offset, mem::size_of::<ThinHeader>());
debug_assert_eq!(_offset, mem::size_of::<usize>());

let obj = allocator.allocate(layout)?;
let header = ThinHeader::from(string.len());
Expand All @@ -95,7 +98,7 @@ impl<A: Allocator> ThinString<A> {

// SAFETY: the offset is at the correct place from the base pointer,
// and again, the memory is valid for writes and Drop is irrelevant.
let data = unsafe { obj.cast::<u8>().as_ptr().add(offset) };
let data = unsafe { obj.cast::<u8>().as_ptr().add(mem::size_of::<usize>()) };

// SAFETY: a new, non-zero sized allocated object must not overlap
// with existing memory by definition. Both regions are at valid for
Expand All @@ -115,9 +118,22 @@ impl<A: Allocator> ThinString<A> {
}

/// Creates a ThinStr that borrows from the ThinString.
#[inline]
pub fn as_thin_str(&self) -> ThinStr {
let storage: &Storage = self.into();
ThinStr::from(storage)
let obj = self.tagged_ptr.ptr();
// SAFETY: ThinString points to a valid header, no mutable references
// ever exist for ThinString, and the lifetime ensures the storage
// lives long enough.
unsafe { ThinStr::from_raw(obj) }
}

#[inline]
pub fn as_storage(&self) -> &Storage {
let obj = self.tagged_ptr.ptr();
// SAFETY: ThinString points to a valid header, no mutable references
// ever exist for ThinString, and the lifetime ensures the storage
// lives long enough.
unsafe { Storage::from_header(obj) }
}
}

Expand Down Expand Up @@ -159,9 +175,7 @@ impl<A: Allocator> Drop for ThinString<A> {

impl<'a, A: Allocator> From<&'a ThinString<A>> for &'a Storage {
fn from(thin_string: &'a ThinString<A>) -> Self {
let obj = thin_string.tagged_ptr.ptr();
// SAFETY: todo
unsafe { Storage::from_header(obj) }
thin_string.as_storage()
}
}
impl<const N: usize> From<&'static ConstStorage<N>> for ThinString<Global> {
Expand Down

0 comments on commit 8bafea5

Please sign in to comment.