Skip to main content

cow_rc_str/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#[cfg(test)]
6mod tests;
7
8use std::borrow::{Borrow, Cow};
9use std::cmp;
10use std::fmt;
11use std::hash;
12use std::marker::PhantomData;
13use std::mem;
14use std::ops::Deref;
15use std::rc::Rc;
16use std::slice;
17use std::str;
18use std::usize;
19
20/// A string that is either shared (heap-allocated and reference-counted) or borrowed.
21///
22/// Equivalent to `enum { Borrowed(&'a str), Shared(Rc<String>) }`, but stored more compactly.
23///
24/// FIXME(https://github.com/rust-lang/rfcs/issues/1230): use an actual enum if/when
25/// the compiler can do this layout optimization.
26pub struct CowRcStr<'a> {
27    /// FIXME: https://github.com/rust-lang/rust/issues/27730 use NonZero or Shared.
28    /// In the meantime we abuse `&'static _` to get the effect of `NonZero<*const _>`.
29    /// `ptr` doesn’t really have the 'static lifetime!
30    ptr: &'static (),
31
32    /// * If `borrowed_len_or_max == usize::MAX`, then `ptr` represents `NonZero<*const String>`
33    ///   from `Rc::into_raw`.
34    ///   The lifetime parameter `'a` is irrelevant in this case.
35    ///
36    /// * Otherwise, `ptr` represents the `NonZero<*const u8>` data component of `&'a str`,
37    ///   and `borrowed_len_or_max` its length.
38    borrowed_len_or_max: usize,
39
40    phantom: PhantomData<Result<&'a str, Rc<String>>>,
41}
42
43fn _static_assert_same_size<'a>() {
44    // "Instantiate" the generic function without calling it.
45    let _ = mem::transmute::<CowRcStr<'a>, Option<CowRcStr<'a>>>;
46}
47
48impl<'a> From<Cow<'a, str>> for CowRcStr<'a> {
49    #[inline]
50    fn from(s: Cow<'a, str>) -> Self {
51        match s {
52            Cow::Borrowed(s) => CowRcStr::from(s),
53            Cow::Owned(s) => CowRcStr::from(s),
54        }
55    }
56}
57
58impl<'a> From<&'a str> for CowRcStr<'a> {
59    #[inline]
60    fn from(s: &'a str) -> Self {
61        let len = s.len();
62        assert!(len < usize::MAX);
63        CowRcStr {
64            ptr: unsafe { &*(s.as_ptr() as *const ()) },
65            borrowed_len_or_max: len,
66            phantom: PhantomData,
67        }
68    }
69}
70
71impl<'a> From<String> for CowRcStr<'a> {
72    #[inline]
73    fn from(s: String) -> Self {
74        CowRcStr::from_rc(Rc::new(s))
75    }
76}
77
78impl<'a> CowRcStr<'a> {
79    #[inline]
80    fn from_rc(s: Rc<String>) -> Self {
81        let ptr = unsafe { &*(Rc::into_raw(s) as *const ()) };
82        CowRcStr {
83            ptr: ptr,
84            borrowed_len_or_max: usize::MAX,
85            phantom: PhantomData,
86        }
87    }
88
89    #[inline]
90    fn unpack(&self) -> Result<&'a str, *const String> {
91        if self.borrowed_len_or_max == usize::MAX {
92            Err(self.ptr as *const () as *const String)
93        } else {
94            unsafe {
95                Ok(str::from_utf8_unchecked(slice::from_raw_parts(
96                    self.ptr as *const () as *const u8,
97                    self.borrowed_len_or_max,
98                )))
99            }
100        }
101    }
102}
103
104impl<'a> Clone for CowRcStr<'a> {
105    #[inline]
106    fn clone(&self) -> Self {
107        match self.unpack() {
108            Err(ptr) => {
109                let rc = unsafe { Rc::from_raw(ptr) };
110                let new_rc = rc.clone();
111                mem::forget(rc); // Don’t actually take ownership of this strong reference
112                CowRcStr::from_rc(new_rc)
113            }
114            Ok(_) => CowRcStr { ..*self },
115        }
116    }
117}
118
119impl<'a> Drop for CowRcStr<'a> {
120    #[inline]
121    fn drop(&mut self) {
122        if let Err(ptr) = self.unpack() {
123            mem::drop(unsafe { Rc::from_raw(ptr) })
124        }
125    }
126}
127
128impl<'a> Deref for CowRcStr<'a> {
129    type Target = str;
130
131    #[inline]
132    fn deref(&self) -> &str {
133        self.unpack().unwrap_or_else(|ptr| unsafe { &**ptr })
134    }
135}
136
137// Boilerplate / trivial impls below.
138
139impl<'a> AsRef<str> for CowRcStr<'a> {
140    #[inline]
141    fn as_ref(&self) -> &str {
142        self
143    }
144}
145
146impl<'a> Borrow<str> for CowRcStr<'a> {
147    #[inline]
148    fn borrow(&self) -> &str {
149        self
150    }
151}
152
153impl<'a> Default for CowRcStr<'a> {
154    #[inline]
155    fn default() -> Self {
156        Self::from("")
157    }
158}
159
160impl<'a> hash::Hash for CowRcStr<'a> {
161    #[inline]
162    fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
163        str::hash(self, hasher)
164    }
165}
166
167impl<'a, T: AsRef<str>> PartialEq<T> for CowRcStr<'a> {
168    #[inline]
169    fn eq(&self, other: &T) -> bool {
170        str::eq(self, other.as_ref())
171    }
172}
173
174impl<'a, T: AsRef<str>> PartialOrd<T> for CowRcStr<'a> {
175    #[inline]
176    fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
177        str::partial_cmp(self, other.as_ref())
178    }
179}
180
181impl<'a> Eq for CowRcStr<'a> {}
182
183impl<'a> Ord for CowRcStr<'a> {
184    #[inline]
185    fn cmp(&self, other: &Self) -> cmp::Ordering {
186        str::cmp(self, other)
187    }
188}
189
190impl<'a> fmt::Display for CowRcStr<'a> {
191    #[inline]
192    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
193        str::fmt(self, formatter)
194    }
195}
196
197impl<'a> fmt::Debug for CowRcStr<'a> {
198    #[inline]
199    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
200        str::fmt(self, formatter)
201    }
202}