pulldown_cmark_fork/
strings.rs1use std::ops::Deref;
2use std::borrow::{ToOwned, Borrow};
3use std::str::from_utf8;
4use std::hash::{Hash, Hasher};
5use std::convert::AsRef;
6use std::fmt;
7
8const MAX_INLINE_STR_LEN: usize = 3 * std::mem::size_of::<isize>() - 1;
9
10#[derive(Debug)]
13pub struct StringTooLongError;
14
15#[derive(Debug, Clone, Copy, Eq)]
16pub struct InlineStr {
17 inner: [u8; MAX_INLINE_STR_LEN],
18}
19
20impl<'a> AsRef<str> for InlineStr {
21 fn as_ref(&self) -> &str {
22 self.deref()
23 }
24}
25
26impl Hash for InlineStr {
27 fn hash<H: Hasher>(&self, state: &mut H) {
28 self.deref().hash(state);
29 }
30}
31
32impl 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
41impl<'a> std::cmp::PartialEq<InlineStr> for InlineStr {
42 fn eq(&self, other: &InlineStr) -> bool {
43 self.deref() == other.deref()
44 }
45}
46
47impl 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
63impl 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
72impl 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)]
79pub enum CowStr<'a> {
80 Boxed(Box<str>),
81 Borrowed(&'a str),
82 Inlined(InlineStr),
83}
84
85impl<'a> AsRef<str> for CowStr<'a> {
86 fn as_ref(&self) -> &str {
87 self.deref()
88 }
89}
90
91impl<'a> Hash for CowStr<'a> {
92 fn hash<H: Hasher>(&self, state: &mut H) {
93 self.deref().hash(state);
94 }
95}
96
97impl<'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
109impl<'a> std::cmp::PartialEq<CowStr<'a>> for CowStr<'a> {
110 fn eq(&self, other: &CowStr) -> bool {
111 self.deref() == other.deref()
112 }
113}
114
115impl<'a> From<&'a str> for CowStr<'a> {
116 fn from(s: &'a str) -> Self {
117 CowStr::Borrowed(s)
118 }
119}
120
121impl<'a> From<String> for CowStr<'a> {
122 fn from(s: String) -> Self {
123 CowStr::Boxed(s.into_boxed_str())
124 }
125}
126
127impl<'a> From<char> for CowStr<'a> {
128 fn from(c: char) -> Self {
129 CowStr::Inlined(c.into())
130 }
131}
132
133impl<'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
145impl<'a> Borrow<str> for CowStr<'a> {
146 fn borrow(&self) -> &str {
147 self.deref()
148 }
149}
150
151impl<'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
161impl<'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)]
168mod 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 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