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