Skip to main content

cssparser/
cow_rc_str.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
5use std::borrow::{Borrow, Cow};
6use std::rc::Rc;
7use std::{cmp, fmt, hash, marker, mem, ops, ptr, slice, str};
8
9/// A string that is either shared (heap-allocated and reference-counted) or borrowed.
10///
11/// Equivalent to `enum { Borrowed(&'a str), Shared(Rc<String>) }`, but stored more compactly.
12///
13/// * If `borrowed_len_or_max == usize::MAX`, then `ptr` represents `NonZero<*const String>`
14///   from `Rc::into_raw`.
15///   The lifetime parameter `'a` is irrelevant in this case.
16///
17/// * Otherwise, `ptr` represents the `NonZero<*const u8>` data component of `&'a str`,
18///   and `borrowed_len_or_max` its length.
19pub struct CowRcStr<'a> {
20    ptr: ptr::NonNull<()>,
21    borrowed_len_or_max: usize,
22
23    phantom: marker::PhantomData<Result<&'a str, Rc<String>>>,
24}
25
26fn _static_assert_same_size() {
27    // "Instantiate" the generic function without calling it.
28    let _ = mem::transmute::<CowRcStr<'_>, Option<CowRcStr<'_>>>;
29}
30
31impl<'a> From<Cow<'a, str>> for CowRcStr<'a> {
32    #[inline]
33    fn from(s: Cow<'a, str>) -> Self {
34        match s {
35            Cow::Borrowed(s) => CowRcStr::from(s),
36            Cow::Owned(s) => CowRcStr::from(s),
37        }
38    }
39}
40
41impl<'a> From<&'a str> for CowRcStr<'a> {
42    #[inline]
43    fn from(s: &'a str) -> Self {
44        let len = s.len();
45        // Guaranteed by https://doc.rust-lang.org/stable/reference/types/numeric.html:
46        //     The theoretical upper bound on object and array size is the maximum isize value
47        // (which by definition is smaller than usize::MAX).
48        debug_assert!(len < usize::MAX);
49        CowRcStr {
50            ptr: unsafe { ptr::NonNull::new_unchecked(s.as_ptr() as *mut ()) },
51            borrowed_len_or_max: len,
52            phantom: marker::PhantomData,
53        }
54    }
55}
56
57impl From<String> for CowRcStr<'_> {
58    #[inline]
59    fn from(s: String) -> Self {
60        CowRcStr::from_rc(Rc::new(s))
61    }
62}
63
64impl<'a> CowRcStr<'a> {
65    #[inline]
66    fn from_rc(s: Rc<String>) -> Self {
67        let ptr = unsafe { ptr::NonNull::new_unchecked(Rc::into_raw(s) as *mut ()) };
68        CowRcStr {
69            ptr,
70            borrowed_len_or_max: usize::MAX,
71            phantom: marker::PhantomData,
72        }
73    }
74
75    #[inline]
76    fn unpack(&self) -> Result<&'a str, *const String> {
77        if self.borrowed_len_or_max == usize::MAX {
78            Err(self.ptr.as_ptr() as *const String)
79        } else {
80            unsafe {
81                Ok(str::from_utf8_unchecked(slice::from_raw_parts(
82                    self.ptr.as_ptr() as *const u8,
83                    self.borrowed_len_or_max,
84                )))
85            }
86        }
87    }
88}
89
90impl Clone for CowRcStr<'_> {
91    #[inline]
92    fn clone(&self) -> Self {
93        match self.unpack() {
94            Err(ptr) => {
95                let rc = unsafe { Rc::from_raw(ptr) };
96                let new_rc = rc.clone();
97                mem::forget(rc); // Don’t actually take ownership of this strong reference
98                CowRcStr::from_rc(new_rc)
99            }
100            Ok(_) => CowRcStr { ..*self },
101        }
102    }
103}
104
105#[cold]
106#[inline(never)]
107unsafe fn drop_slow(ptr: *const String) {
108    unsafe { mem::drop(Rc::from_raw(ptr)) }
109}
110
111impl Drop for CowRcStr<'_> {
112    #[inline]
113    fn drop(&mut self) {
114        if let Err(ptr) = self.unpack() {
115            unsafe { drop_slow(ptr) }
116        }
117    }
118}
119
120impl ops::Deref for CowRcStr<'_> {
121    type Target = str;
122
123    #[inline]
124    fn deref(&self) -> &str {
125        self.unpack().unwrap_or_else(|ptr| unsafe { &**ptr })
126    }
127}
128
129// Boilerplate / trivial impls below.
130
131impl AsRef<str> for CowRcStr<'_> {
132    #[inline]
133    fn as_ref(&self) -> &str {
134        self
135    }
136}
137
138impl Borrow<str> for CowRcStr<'_> {
139    #[inline]
140    fn borrow(&self) -> &str {
141        self
142    }
143}
144
145impl Default for CowRcStr<'_> {
146    #[inline]
147    fn default() -> Self {
148        Self::from("")
149    }
150}
151
152impl hash::Hash for CowRcStr<'_> {
153    #[inline]
154    fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
155        str::hash(self, hasher)
156    }
157}
158
159impl<T: AsRef<str>> PartialEq<T> for CowRcStr<'_> {
160    #[inline]
161    fn eq(&self, other: &T) -> bool {
162        str::eq(self, other.as_ref())
163    }
164}
165
166impl<T: AsRef<str>> PartialOrd<T> for CowRcStr<'_> {
167    #[inline]
168    fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
169        str::partial_cmp(self, other.as_ref())
170    }
171}
172
173impl Eq for CowRcStr<'_> {}
174
175impl Ord for CowRcStr<'_> {
176    #[inline]
177    fn cmp(&self, other: &Self) -> cmp::Ordering {
178        str::cmp(self, other)
179    }
180}
181
182impl fmt::Display for CowRcStr<'_> {
183    #[inline]
184    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
185        str::fmt(self, formatter)
186    }
187}
188
189impl fmt::Debug for CowRcStr<'_> {
190    #[inline]
191    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
192        str::fmt(self, formatter)
193    }
194}