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        assert!(len < usize::MAX);
46        CowRcStr {
47            ptr: unsafe { ptr::NonNull::new_unchecked(s.as_ptr() as *mut ()) },
48            borrowed_len_or_max: len,
49            phantom: marker::PhantomData,
50        }
51    }
52}
53
54impl From<String> for CowRcStr<'_> {
55    #[inline]
56    fn from(s: String) -> Self {
57        CowRcStr::from_rc(Rc::new(s))
58    }
59}
60
61impl<'a> CowRcStr<'a> {
62    #[inline]
63    fn from_rc(s: Rc<String>) -> Self {
64        let ptr = unsafe { ptr::NonNull::new_unchecked(Rc::into_raw(s) as *mut ()) };
65        CowRcStr {
66            ptr,
67            borrowed_len_or_max: usize::MAX,
68            phantom: marker::PhantomData,
69        }
70    }
71
72    #[inline]
73    fn unpack(&self) -> Result<&'a str, *const String> {
74        if self.borrowed_len_or_max == usize::MAX {
75            Err(self.ptr.as_ptr() as *const String)
76        } else {
77            unsafe {
78                Ok(str::from_utf8_unchecked(slice::from_raw_parts(
79                    self.ptr.as_ptr() as *const u8,
80                    self.borrowed_len_or_max,
81                )))
82            }
83        }
84    }
85}
86
87impl Clone for CowRcStr<'_> {
88    #[inline]
89    fn clone(&self) -> Self {
90        match self.unpack() {
91            Err(ptr) => {
92                let rc = unsafe { Rc::from_raw(ptr) };
93                let new_rc = rc.clone();
94                mem::forget(rc); // Don’t actually take ownership of this strong reference
95                CowRcStr::from_rc(new_rc)
96            }
97            Ok(_) => CowRcStr { ..*self },
98        }
99    }
100}
101
102impl Drop for CowRcStr<'_> {
103    #[inline]
104    fn drop(&mut self) {
105        if let Err(ptr) = self.unpack() {
106            mem::drop(unsafe { Rc::from_raw(ptr) })
107        }
108    }
109}
110
111impl ops::Deref for CowRcStr<'_> {
112    type Target = str;
113
114    #[inline]
115    fn deref(&self) -> &str {
116        self.unpack().unwrap_or_else(|ptr| unsafe { &**ptr })
117    }
118}
119
120// Boilerplate / trivial impls below.
121
122impl AsRef<str> for CowRcStr<'_> {
123    #[inline]
124    fn as_ref(&self) -> &str {
125        self
126    }
127}
128
129impl Borrow<str> for CowRcStr<'_> {
130    #[inline]
131    fn borrow(&self) -> &str {
132        self
133    }
134}
135
136impl Default for CowRcStr<'_> {
137    #[inline]
138    fn default() -> Self {
139        Self::from("")
140    }
141}
142
143impl hash::Hash for CowRcStr<'_> {
144    #[inline]
145    fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
146        str::hash(self, hasher)
147    }
148}
149
150impl<T: AsRef<str>> PartialEq<T> for CowRcStr<'_> {
151    #[inline]
152    fn eq(&self, other: &T) -> bool {
153        str::eq(self, other.as_ref())
154    }
155}
156
157impl<T: AsRef<str>> PartialOrd<T> for CowRcStr<'_> {
158    #[inline]
159    fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
160        str::partial_cmp(self, other.as_ref())
161    }
162}
163
164impl Eq for CowRcStr<'_> {}
165
166impl Ord for CowRcStr<'_> {
167    #[inline]
168    fn cmp(&self, other: &Self) -> cmp::Ordering {
169        str::cmp(self, other)
170    }
171}
172
173impl fmt::Display for CowRcStr<'_> {
174    #[inline]
175    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
176        str::fmt(self, formatter)
177    }
178}
179
180impl fmt::Debug for CowRcStr<'_> {
181    #[inline]
182    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
183        str::fmt(self, formatter)
184    }
185}