Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 17 additions & 30 deletions src/cow_rc_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,24 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::borrow::{Borrow, Cow};
use std::cmp;
use std::fmt;
use std::hash;
use std::marker::PhantomData;
use std::mem;
use std::ops::Deref;
use std::rc::Rc;
use std::slice;
use std::str;
use std::{cmp, fmt, hash, marker, mem, ops, slice, str, ptr};

/// A string that is either shared (heap-allocated and reference-counted) or borrowed.
///
/// Equivalent to `enum { Borrowed(&'a str), Shared(Rc<String>) }`, but stored more compactly.
///
/// FIXME(https://github.com/rust-lang/rfcs/issues/1230): use an actual enum if/when
/// the compiler can do this layout optimization.
/// * If `borrowed_len_or_max == usize::MAX`, then `ptr` represents `NonZero<*const String>`
/// from `Rc::into_raw`.
/// The lifetime parameter `'a` is irrelevant in this case.
///
/// * Otherwise, `ptr` represents the `NonZero<*const u8>` data component of `&'a str`,
/// and `borrowed_len_or_max` its length.
pub struct CowRcStr<'a> {
/// FIXME: https://github.com/rust-lang/rust/issues/27730 use NonZero or Shared.
/// In the meantime we abuse `&'static _` to get the effect of `NonZero<*const _>`.
/// `ptr` doesn’t really have the 'static lifetime!
ptr: &'static (),

/// * If `borrowed_len_or_max == usize::MAX`, then `ptr` represents `NonZero<*const String>`
/// from `Rc::into_raw`.
/// The lifetime parameter `'a` is irrelevant in this case.
///
/// * Otherwise, `ptr` represents the `NonZero<*const u8>` data component of `&'a str`,
/// and `borrowed_len_or_max` its length.
ptr: ptr::NonNull<()>,
borrowed_len_or_max: usize,

phantom: PhantomData<Result<&'a str, Rc<String>>>,
phantom: marker::PhantomData<Result<&'a str, Rc<String>>>,
}

fn _static_assert_same_size<'a>() {
Expand All @@ -57,9 +44,9 @@ impl<'a> From<&'a str> for CowRcStr<'a> {
let len = s.len();
assert!(len < usize::MAX);
CowRcStr {
ptr: unsafe { &*(s.as_ptr() as *const ()) },
ptr: unsafe { ptr::NonNull::new_unchecked(s.as_ptr() as *mut ()) },
borrowed_len_or_max: len,
phantom: PhantomData,
phantom: marker::PhantomData,
}
}
}
Expand All @@ -74,22 +61,22 @@ impl<'a> From<String> for CowRcStr<'a> {
impl<'a> CowRcStr<'a> {
#[inline]
fn from_rc(s: Rc<String>) -> Self {
let ptr = unsafe { &*(Rc::into_raw(s) as *const ()) };
let ptr = unsafe { ptr::NonNull::new_unchecked(Rc::into_raw(s) as *mut ()) };
CowRcStr {
ptr: ptr,
ptr,
borrowed_len_or_max: usize::MAX,
phantom: PhantomData,
phantom: marker::PhantomData,
}
}

#[inline]
fn unpack(&self) -> Result<&'a str, *const String> {
if self.borrowed_len_or_max == usize::MAX {
Err(self.ptr as *const () as *const String)
Err(self.ptr.as_ptr() as *const String)
} else {
unsafe {
Ok(str::from_utf8_unchecked(slice::from_raw_parts(
self.ptr as *const () as *const u8,
self.ptr.as_ptr() as *const u8,
self.borrowed_len_or_max,
)))
}
Expand Down Expand Up @@ -121,7 +108,7 @@ impl<'a> Drop for CowRcStr<'a> {
}
}

impl<'a> Deref for CowRcStr<'a> {
impl<'a> ops::Deref for CowRcStr<'a> {
type Target = str;

#[inline]
Expand Down