]> git.proxmox.com Git - rustc.git/blob - vendor/pulldown-cmark-0.7.2/src/strings.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / vendor / pulldown-cmark-0.7.2 / src / strings.rs
1 use std::borrow::{Borrow, ToOwned};
2 use std::convert::{AsRef, TryFrom};
3 use std::fmt;
4 use std::hash::{Hash, Hasher};
5 use std::ops::Deref;
6 use std::str::from_utf8;
7
8 const MAX_INLINE_STR_LEN: usize = 3 * std::mem::size_of::<isize>() - 1;
9
10 /// Returned when trying to convert a `&str` into a `InlineStr`
11 /// but it fails because it doesn't fit.
12 #[derive(Debug)]
13 pub struct StringTooLongError;
14
15 /// An inline string that can contain almost three words
16 /// of utf-8 text.
17 #[derive(Debug, Clone, Copy, Eq)]
18 pub struct InlineStr {
19 inner: [u8; MAX_INLINE_STR_LEN],
20 }
21
22 impl<'a> AsRef<str> for InlineStr {
23 fn as_ref(&self) -> &str {
24 self.deref()
25 }
26 }
27
28 impl Hash for InlineStr {
29 fn hash<H: Hasher>(&self, state: &mut H) {
30 self.deref().hash(state);
31 }
32 }
33
34 impl From<char> for InlineStr {
35 fn from(c: char) -> Self {
36 let mut inner = [0u8; MAX_INLINE_STR_LEN];
37 c.encode_utf8(&mut inner);
38 inner[MAX_INLINE_STR_LEN - 1] = c.len_utf8() as u8;
39 Self { inner }
40 }
41 }
42
43 impl<'a> std::cmp::PartialEq<InlineStr> for InlineStr {
44 fn eq(&self, other: &InlineStr) -> bool {
45 self.deref() == other.deref()
46 }
47 }
48
49 impl TryFrom<&str> for InlineStr {
50 type Error = StringTooLongError;
51
52 fn try_from(s: &str) -> Result<InlineStr, StringTooLongError> {
53 let len = s.len();
54 if len < MAX_INLINE_STR_LEN {
55 let mut inner = [0u8; MAX_INLINE_STR_LEN];
56 inner[..len].copy_from_slice(s.as_bytes());
57 inner[MAX_INLINE_STR_LEN - 1] = len as u8;
58 Ok(Self { inner })
59 } else {
60 Err(StringTooLongError)
61 }
62 }
63 }
64
65 impl Deref for InlineStr {
66 type Target = str;
67
68 fn deref(&self) -> &str {
69 let len = self.inner[MAX_INLINE_STR_LEN - 1] as usize;
70 from_utf8(&self.inner[..len]).unwrap()
71 }
72 }
73
74 impl fmt::Display for InlineStr {
75 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 write!(f, "{}", self.as_ref())
77 }
78 }
79
80 /// A copy-on-write string that can be owned, borrowed
81 /// or inlined.
82 ///
83 /// It is three words long.
84 #[derive(Debug, Eq)]
85 pub enum CowStr<'a> {
86 /// An owned, immutable string.
87 Boxed(Box<str>),
88 /// A borrowed string.
89 Borrowed(&'a str),
90 /// A short inline string.
91 Inlined(InlineStr),
92 }
93
94 impl<'a> AsRef<str> for CowStr<'a> {
95 fn as_ref(&self) -> &str {
96 self.deref()
97 }
98 }
99
100 impl<'a> Hash for CowStr<'a> {
101 fn hash<H: Hasher>(&self, state: &mut H) {
102 self.deref().hash(state);
103 }
104 }
105
106 impl<'a> std::clone::Clone for CowStr<'a> {
107 fn clone(&self) -> Self {
108 match self {
109 CowStr::Boxed(s) => match InlineStr::try_from(&**s) {
110 Ok(inline) => CowStr::Inlined(inline),
111 Err(..) => CowStr::Boxed(s.clone()),
112 },
113 CowStr::Borrowed(s) => CowStr::Borrowed(s),
114 CowStr::Inlined(s) => CowStr::Inlined(*s),
115 }
116 }
117 }
118
119 impl<'a> std::cmp::PartialEq<CowStr<'a>> for CowStr<'a> {
120 fn eq(&self, other: &CowStr) -> bool {
121 self.deref() == other.deref()
122 }
123 }
124
125 impl<'a> From<&'a str> for CowStr<'a> {
126 fn from(s: &'a str) -> Self {
127 CowStr::Borrowed(s)
128 }
129 }
130
131 impl<'a> From<String> for CowStr<'a> {
132 fn from(s: String) -> Self {
133 CowStr::Boxed(s.into_boxed_str())
134 }
135 }
136
137 impl<'a> From<char> for CowStr<'a> {
138 fn from(c: char) -> Self {
139 CowStr::Inlined(c.into())
140 }
141 }
142
143 impl<'a> Deref for CowStr<'a> {
144 type Target = str;
145
146 fn deref(&self) -> &str {
147 match self {
148 CowStr::Boxed(ref b) => &*b,
149 CowStr::Borrowed(b) => b,
150 CowStr::Inlined(ref s) => s.deref(),
151 }
152 }
153 }
154
155 impl<'a> Borrow<str> for CowStr<'a> {
156 fn borrow(&self) -> &str {
157 self.deref()
158 }
159 }
160
161 impl<'a> CowStr<'a> {
162 pub fn into_string(self) -> String {
163 match self {
164 CowStr::Boxed(b) => b.into(),
165 CowStr::Borrowed(b) => b.to_owned(),
166 CowStr::Inlined(s) => s.deref().to_owned(),
167 }
168 }
169 }
170
171 impl<'a> fmt::Display for CowStr<'a> {
172 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173 write!(f, "{}", self.as_ref())
174 }
175 }
176
177 #[cfg(test)]
178 mod test_special_string {
179 use super::*;
180
181 #[test]
182 fn inlinestr_ascii() {
183 let s: InlineStr = 'a'.into();
184 assert_eq!("a", s.deref());
185 }
186
187 #[test]
188 fn inlinestr_unicode() {
189 let s: InlineStr = '🍔'.into();
190 assert_eq!("🍔", s.deref());
191 }
192
193 #[test]
194 fn cowstr_size() {
195 let size = std::mem::size_of::<CowStr>();
196 let word_size = std::mem::size_of::<isize>();
197 assert_eq!(3 * word_size, size);
198 }
199
200 #[test]
201 fn cowstr_char_to_string() {
202 let c = '藏';
203 let smort: CowStr = c.into();
204 let owned: String = smort.to_string();
205 let expected = "藏".to_owned();
206 assert_eq!(expected, owned);
207 }
208
209 #[test]
210 fn max_inline_str_len_atleast_five() {
211 // we need 4 bytes to store a char and then one more to store
212 // its length
213 assert!(MAX_INLINE_STR_LEN >= 5);
214 }
215
216 #[test]
217 #[cfg(target_pointer_width = "64")]
218 fn inlinestr_fits_twentytwo() {
219 let s = "0123456789abcdefghijkl";
220 let stack_str = InlineStr::try_from(s).unwrap();
221 assert_eq!(stack_str.deref(), s);
222 }
223
224 #[test]
225 #[cfg(target_pointer_width = "64")]
226 fn inlinestr_not_fits_twentythree() {
227 let s = "0123456789abcdefghijklm";
228 let _stack_str = InlineStr::try_from(s).unwrap_err();
229 }
230
231 #[test]
232 #[cfg(target_pointer_width = "64")]
233 fn small_boxed_str_clones_to_stack() {
234 let s = "0123456789abcde".to_owned();
235 let smort: CowStr = s.into();
236 let smort_clone = smort.clone();
237
238 if let CowStr::Inlined(..) = smort_clone {
239 } else {
240 panic!("Expected a Inlined variant!");
241 }
242 }
243 }