1use alloc::{
2 borrow::{Cow, ToOwned},
3 boxed::Box,
4 string::{String, ToString},
5};
6use core::{
7 borrow::Borrow,
8 fmt,
9 hash::{Hash, Hasher},
10 ops::Deref,
11 str::from_utf8,
12};
13
14const MAX_INLINE_STR_LEN: usize = 3 * core::mem::size_of::<isize>() - 2;
15
16#[derive(Debug)]
19pub struct StringTooLongError;
20
21#[derive(Debug, Clone, Copy, Eq)]
24pub struct InlineStr {
25 inner: [u8; MAX_INLINE_STR_LEN],
26 len: u8,
27}
28
29impl AsRef<str> for InlineStr {
30 fn as_ref(&self) -> &str {
31 self.deref()
32 }
33}
34
35impl Hash for InlineStr {
36 fn hash<H: Hasher>(&self, state: &mut H) {
37 self.deref().hash(state);
38 }
39}
40
41impl From<char> for InlineStr {
42 fn from(c: char) -> Self {
43 let mut inner = [0u8; MAX_INLINE_STR_LEN];
44 c.encode_utf8(&mut inner);
45 let len = c.len_utf8() as u8;
46 Self { inner, len }
47 }
48}
49
50impl core::cmp::PartialEq<InlineStr> for InlineStr {
51 fn eq(&self, other: &InlineStr) -> bool {
52 self.deref() == other.deref()
53 }
54}
55
56impl TryFrom<&str> for InlineStr {
57 type Error = StringTooLongError;
58
59 fn try_from(s: &str) -> Result<InlineStr, StringTooLongError> {
60 let len = s.len();
61 if len <= MAX_INLINE_STR_LEN {
62 let mut inner = [0u8; MAX_INLINE_STR_LEN];
63 inner[..len].copy_from_slice(s.as_bytes());
64 let len = len as u8;
65 Ok(Self { inner, len })
66 } else {
67 Err(StringTooLongError)
68 }
69 }
70}
71
72impl Deref for InlineStr {
73 type Target = str;
74
75 fn deref(&self) -> &str {
76 let len = self.len as usize;
77 from_utf8(&self.inner[..len]).unwrap()
78 }
79}
80
81impl fmt::Display for InlineStr {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 write!(f, "{}", self.as_ref())
84 }
85}
86
87#[derive(Debug, Eq)]
92pub enum CowStr<'a> {
93 Boxed(Box<str>),
95 Borrowed(&'a str),
97 Inlined(InlineStr),
99}
100
101#[cfg(feature = "serde")]
102mod serde_impl {
103 use core::fmt;
104
105 use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
106
107 use super::CowStr;
108
109 impl<'a> Serialize for CowStr<'a> {
110 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
111 where
112 S: Serializer,
113 {
114 serializer.serialize_str(self.as_ref())
115 }
116 }
117
118 struct CowStrVisitor;
119
120 impl<'de> de::Visitor<'de> for CowStrVisitor {
121 type Value = CowStr<'de>;
122
123 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
124 formatter.write_str("a string")
125 }
126
127 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
128 where
129 E: de::Error,
130 {
131 Ok(CowStr::Borrowed(v))
132 }
133
134 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
135 where
136 E: de::Error,
137 {
138 match v.try_into() {
139 Ok(it) => Ok(CowStr::Inlined(it)),
140 Err(_) => Ok(CowStr::Boxed(String::from(v).into_boxed_str())),
141 }
142 }
143
144 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
145 where
146 E: de::Error,
147 {
148 Ok(CowStr::Boxed(v.into_boxed_str()))
149 }
150 }
151
152 impl<'a, 'de: 'a> Deserialize<'de> for CowStr<'a> {
153 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
154 where
155 D: Deserializer<'de>,
156 {
157 deserializer.deserialize_str(CowStrVisitor)
158 }
159 }
160}
161
162impl<'a> AsRef<str> for CowStr<'a> {
163 fn as_ref(&self) -> &str {
164 self.deref()
165 }
166}
167
168impl<'a> Hash for CowStr<'a> {
169 fn hash<H: Hasher>(&self, state: &mut H) {
170 self.deref().hash(state);
171 }
172}
173
174impl<'a> core::clone::Clone for CowStr<'a> {
175 fn clone(&self) -> Self {
176 match self {
177 CowStr::Boxed(s) => match InlineStr::try_from(&**s) {
178 Ok(inline) => CowStr::Inlined(inline),
179 Err(..) => CowStr::Boxed(s.clone()),
180 },
181 CowStr::Borrowed(s) => CowStr::Borrowed(s),
182 CowStr::Inlined(s) => CowStr::Inlined(*s),
183 }
184 }
185}
186
187impl<'a> core::cmp::PartialEq<CowStr<'a>> for CowStr<'a> {
188 fn eq(&self, other: &CowStr<'_>) -> bool {
189 self.deref() == other.deref()
190 }
191}
192
193impl<'a> From<&'a str> for CowStr<'a> {
194 fn from(s: &'a str) -> Self {
195 CowStr::Borrowed(s)
196 }
197}
198
199impl<'a> From<String> for CowStr<'a> {
200 fn from(s: String) -> Self {
201 CowStr::Boxed(s.into_boxed_str())
202 }
203}
204
205impl<'a> From<char> for CowStr<'a> {
206 fn from(c: char) -> Self {
207 CowStr::Inlined(c.into())
208 }
209}
210
211impl<'a> From<Cow<'a, str>> for CowStr<'a> {
212 fn from(s: Cow<'a, str>) -> Self {
213 match s {
214 Cow::Borrowed(s) => CowStr::Borrowed(s),
215 Cow::Owned(s) => CowStr::Boxed(s.into_boxed_str()),
216 }
217 }
218}
219
220impl<'a> From<CowStr<'a>> for Cow<'a, str> {
221 fn from(s: CowStr<'a>) -> Self {
222 match s {
223 CowStr::Boxed(s) => Cow::Owned(s.to_string()),
224 CowStr::Inlined(s) => Cow::Owned(s.to_string()),
225 CowStr::Borrowed(s) => Cow::Borrowed(s),
226 }
227 }
228}
229
230impl<'a> From<Cow<'a, char>> for CowStr<'a> {
231 fn from(s: Cow<'a, char>) -> Self {
232 CowStr::Inlined(InlineStr::from(*s))
233 }
234}
235
236impl<'a> From<CowStr<'a>> for String {
237 fn from(s: CowStr<'a>) -> Self {
238 match s {
239 CowStr::Boxed(s) => s.into(),
240 CowStr::Inlined(s) => s.as_ref().into(),
241 CowStr::Borrowed(s) => s.into(),
242 }
243 }
244}
245
246impl<'a> Deref for CowStr<'a> {
247 type Target = str;
248
249 fn deref(&self) -> &str {
250 match self {
251 CowStr::Boxed(ref b) => b,
252 CowStr::Borrowed(b) => b,
253 CowStr::Inlined(ref s) => s.deref(),
254 }
255 }
256}
257
258impl<'a> Borrow<str> for CowStr<'a> {
259 fn borrow(&self) -> &str {
260 self.deref()
261 }
262}
263
264impl<'a> CowStr<'a> {
265 pub fn into_string(self) -> String {
266 match self {
267 CowStr::Boxed(b) => b.into(),
268 CowStr::Borrowed(b) => b.to_owned(),
269 CowStr::Inlined(s) => s.deref().to_owned(),
270 }
271 }
272
273 pub fn into_static(self) -> CowStr<'static> {
274 match self {
275 CowStr::Boxed(b) => CowStr::Boxed(b),
276 CowStr::Borrowed(b) => match InlineStr::try_from(b) {
277 Ok(inline) => CowStr::Inlined(inline),
278 Err(_) => CowStr::Boxed(b.into()),
279 },
280 CowStr::Inlined(s) => CowStr::Inlined(s),
281 }
282 }
283}
284
285impl<'a> fmt::Display for CowStr<'a> {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 write!(f, "{}", self.as_ref())
288 }
289}
290
291#[cfg(test)]
292mod test_special_string {
293 use alloc::{
294 borrow::ToOwned,
295 string::{String, ToString},
296 };
297
298 use super::*;
299
300 #[test]
301 fn inlinestr_ascii() {
302 let s: InlineStr = 'a'.into();
303 assert_eq!("a", s.deref());
304 }
305
306 #[test]
307 fn inlinestr_unicode() {
308 let s: InlineStr = '🍔'.into();
309 assert_eq!("🍔", s.deref());
310 }
311
312 #[test]
313 fn cowstr_size() {
314 let size = core::mem::size_of::<CowStr>();
315 let word_size = core::mem::size_of::<isize>();
316 assert_eq!(3 * word_size, size);
317 }
318
319 #[test]
320 fn cowstr_char_to_string() {
321 let c = '藏';
322 let smort: CowStr = c.into();
323 let owned: String = smort.to_string();
324 let expected = "藏".to_owned();
325 assert_eq!(expected, owned);
326 }
327
328 #[test]
329 #[cfg(target_pointer_width = "64")]
330 fn inlinestr_fits_twentytwo() {
331 let s = "0123456789abcdefghijkl";
332 let stack_str = InlineStr::try_from(s).unwrap();
333 assert_eq!(stack_str.deref(), s);
334 }
335
336 #[test]
337 #[cfg(target_pointer_width = "64")]
338 fn inlinestr_not_fits_twentythree() {
339 let s = "0123456789abcdefghijklm";
340 let _stack_str = InlineStr::try_from(s).unwrap_err();
341 }
342
343 #[test]
344 #[cfg(target_pointer_width = "64")]
345 fn small_boxed_str_clones_to_stack() {
346 let s = "0123456789abcde".to_owned();
347 let smort: CowStr = s.into();
348 let smort_clone = smort.clone();
349
350 if let CowStr::Inlined(..) = smort_clone {
351 } else {
352 panic!("Expected a Inlined variant!");
353 }
354 }
355
356 #[test]
357 fn cow_to_cow_str() {
358 let s = "some text";
359 let cow = Cow::Borrowed(s);
360 let actual = CowStr::from(cow);
361 let expected = CowStr::Borrowed(s);
362 assert_eq!(actual, expected);
363 assert!(variant_eq(&actual, &expected));
364
365 let s = "some text".to_string();
366 let cow: Cow<str> = Cow::Owned(s.clone());
367 let actual = CowStr::from(cow);
368 let expected = CowStr::Boxed(s.into_boxed_str());
369 assert_eq!(actual, expected);
370 assert!(variant_eq(&actual, &expected));
371 }
372
373 #[test]
374 fn cow_str_to_cow() {
375 let s = "some text";
376 let cow_str = CowStr::Borrowed(s);
377 let actual = Cow::from(cow_str);
378 let expected = Cow::Borrowed(s);
379 assert_eq!(actual, expected);
380 assert!(variant_eq(&actual, &expected));
381
382 let s = "s";
383 let inline_str: InlineStr = InlineStr::try_from(s).unwrap();
384 let cow_str = CowStr::Inlined(inline_str);
385 let actual = Cow::from(cow_str);
386 let expected: Cow<str> = Cow::Owned(s.to_string());
387 assert_eq!(actual, expected);
388 assert!(variant_eq(&actual, &expected));
389
390 let s = "s";
391 let cow_str = CowStr::Boxed(s.to_string().into_boxed_str());
392 let actual = Cow::from(cow_str);
393 let expected: Cow<str> = Cow::Owned(s.to_string());
394 assert_eq!(actual, expected);
395 assert!(variant_eq(&actual, &expected));
396 }
397
398 #[test]
399 fn cow_str_to_string() {
400 let s = "some text";
401 let cow_str = CowStr::Borrowed(s);
402 let actual = String::from(cow_str);
403 let expected = String::from("some text");
404 assert_eq!(actual, expected);
405
406 let s = "s";
407 let inline_str: InlineStr = InlineStr::try_from(s).unwrap();
408 let cow_str = CowStr::Inlined(inline_str);
409 let actual = String::from(cow_str);
410 let expected = String::from("s");
411 assert_eq!(actual, expected);
412
413 let s = "s";
414 let cow_str = CowStr::Boxed(s.to_string().into_boxed_str());
415 let actual = String::from(cow_str);
416 let expected = String::from("s");
417 assert_eq!(actual, expected);
418 }
419
420 #[test]
421 fn cow_char_to_cow_str() {
422 let c = 'c';
423 let cow: Cow<char> = Cow::Owned(c);
424 let actual = CowStr::from(cow);
425 let expected = CowStr::Inlined(InlineStr::from(c));
426 assert_eq!(actual, expected);
427 assert!(variant_eq(&actual, &expected));
428
429 let c = 'c';
430 let cow: Cow<char> = Cow::Borrowed(&c);
431 let actual = CowStr::from(cow);
432 let expected = CowStr::Inlined(InlineStr::from(c));
433 assert_eq!(actual, expected);
434 assert!(variant_eq(&actual, &expected));
435 }
436
437 fn variant_eq<T>(a: &T, b: &T) -> bool {
438 core::mem::discriminant(a) == core::mem::discriminant(b)
439 }
440}