Skip to content

Commit

Permalink
Merge pull request #8 from maciejhirsz/no-ptr-casting
Browse files Browse the repository at this point in the history
Avoid pointer casting
  • Loading branch information
maciejhirsz authored Mar 14, 2020
2 parents f5f1264 + ad2d71a commit a167927
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 40 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "beef"
version = "0.1.5"
version = "0.2.0"
authors = ["Maciej Hirsz <hello@maciej.codes>"]
edition = "2018"
description = "More compact Cow"
Expand Down
82 changes: 43 additions & 39 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use core::mem;
use core::num::NonZeroUsize;
use core::ptr::NonNull;
use core::ptr::{slice_from_raw_parts_mut, NonNull};

/// A clone-on-write smart pointer, mostly compatible with [`std::borrow::Cow`](https://doc.rust-lang.org/std/borrow/enum.Cow.html).
#[derive(Eq)]
Expand All @@ -54,53 +54,64 @@ pub struct Cow<'a, T: Beef + ?Sized + 'a> {
/// + `T::Owned` with `capacity` of `0` does not allocate memory.
/// + `T::Owned` can be reconstructed from `*mut T` borrowed out of it, plus capacity.
pub unsafe trait Beef: ToOwned {
/// Get capacity of owned variant of `T`. Return `None` for `0`.
/// Returning invalid capacity will lead to undefined behavior.
fn capacity(owned: &Self::Owned) -> Option<NonZeroUsize>;

/// Convert `&mut T::Owned` to `*mut T`, stripping `capacity`.
unsafe fn owned_ptr(owned: &mut Self::Owned) -> NonNull<Self>;
/// Convert `T::Owned` to `NonNull<T>` and capacity.
/// Return `None` for `0` capacity.
fn owned_into_parts(owned: Self::Owned) -> (NonNull<Self>, Option<NonZeroUsize>);

/// Rebuild `T::Owned` from `NonNull<T>` and `capacity`. This can be done by the likes
/// of [`Vec::from_raw_parts`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.from_raw_parts).
unsafe fn rebuild(ptr: NonNull<Self>, capacity: usize) -> Self::Owned;
unsafe fn owned_from_parts(ptr: NonNull<Self>, capacity: NonZeroUsize) -> Self::Owned;
}

unsafe impl Beef for str {
#[inline]
fn capacity(owned: &String) -> Option<NonZeroUsize> {
NonZeroUsize::new(owned.capacity())
}
fn owned_into_parts(owned: String) -> (NonNull<str>, Option<NonZeroUsize>) {
// Convert to `String::into_raw_parts` once stabilized
let mut owned = mem::ManuallyDrop::new(owned);
let ptr = slice_from_raw_parts_mut(
owned.as_mut_ptr(),
owned.len(),
);

#[inline]
unsafe fn owned_ptr(owned: &mut String) -> NonNull<str> {
NonNull::new_unchecked(owned.as_mut_str() as *mut str)
(
unsafe { NonNull::new_unchecked(ptr as *mut str) },
NonZeroUsize::new(owned.capacity()),
)
}

#[inline]
unsafe fn rebuild(mut ptr: NonNull<Self>, capacity: usize) -> String {
unsafe fn owned_from_parts(mut ptr: NonNull<Self>, capacity: NonZeroUsize) -> String {
String::from_utf8_unchecked(Vec::from_raw_parts(
ptr.as_mut().as_mut_ptr(),
ptr.as_mut().len(),
capacity,
capacity.get(),
))
}
}

unsafe impl<T: Clone> Beef for [T] {
#[inline]
fn capacity(owned: &Vec<T>) -> Option<NonZeroUsize> {
NonZeroUsize::new(owned.capacity())
}
fn owned_into_parts(owned: Vec<T>) -> (NonNull<[T]>, Option<NonZeroUsize>) {
// Convert to `Vec::into_raw_parts` once stabilized
let mut owned = mem::ManuallyDrop::new(owned);
let ptr = slice_from_raw_parts_mut(
owned.as_mut_ptr(),
owned.len(),
);

#[inline]
unsafe fn owned_ptr(owned: &mut Vec<T>) -> NonNull<[T]> {
NonNull::new_unchecked(owned.as_mut_slice() as *mut [T])
(
unsafe { NonNull::new_unchecked(ptr) },
NonZeroUsize::new(owned.capacity()),
)
}

#[inline]
unsafe fn rebuild(mut ptr: NonNull<Self>, capacity: usize) -> Vec<T> {
Vec::from_raw_parts(ptr.as_mut().as_mut_ptr(), ptr.as_mut().len(), capacity)
unsafe fn owned_from_parts(mut ptr: NonNull<Self>, capacity: NonZeroUsize) -> Vec<T> {
Vec::from_raw_parts(
ptr.as_mut().as_mut_ptr(),
ptr.as_mut().len(),
capacity.get(),
)
}
}

Expand All @@ -110,11 +121,8 @@ where
{
/// Owned data.
#[inline]
pub fn owned(mut val: B::Owned) -> Self {
let capacity = B::capacity(&val);
let inner = unsafe { B::owned_ptr(&mut val) };

mem::forget(val);
pub fn owned(val: B::Owned) -> Self {
let (inner, capacity) = B::owned_into_parts(val);

Cow {
inner,
Expand Down Expand Up @@ -165,15 +173,11 @@ where
/// Clones the data if it is not already owned.
#[inline]
pub fn into_owned(self) -> T::Owned {
let Cow {
inner, capacity, ..
} = self;

mem::forget(self);
let cow = mem::ManuallyDrop::new(self);

match capacity {
Some(capacity) => unsafe { T::rebuild(inner, capacity.get()) },
None => unsafe { inner.as_ref() }.to_owned(),
match cow.capacity {
Some(capacity) => unsafe { T::owned_from_parts(cow.inner, capacity) },
None => unsafe { cow.inner.as_ref() }.to_owned(),
}
}

Expand Down Expand Up @@ -228,7 +232,7 @@ where
#[inline]
fn drop(&mut self) {
if let Some(capacity) = self.capacity {
mem::drop(unsafe { T::rebuild(self.inner, capacity.get()) });
mem::drop(unsafe { T::owned_from_parts(self.inner, capacity) });
}
}
}
Expand Down Expand Up @@ -304,7 +308,7 @@ where
mem::forget(cow);

match capacity {
Some(capacity) => StdCow::Owned(unsafe { T::rebuild(inner, capacity.get()) }),
Some(capacity) => StdCow::Owned(unsafe { T::owned_from_parts(inner, capacity) }),
None => StdCow::Borrowed(unsafe { &*inner.as_ptr() }),
}
}
Expand Down

0 comments on commit a167927

Please sign in to comment.