1use core::mem::size_of;
2use std::borrow::Borrow;
3use std::fmt::Display;
4use std::ops::Deref;
5use std::path::Path;
6use std::sync::Arc;
7
8use const_format::assertcp_eq;
9use num_enum::TryFromPrimitive;
10
11#[derive(Clone)]
13pub struct ImStr(Repr);
14
15const INLINE_BYTES: usize = if cfg!(target_pointer_width = "64") { 23 } else { 11 };
16#[derive(Clone)]
17pub(crate) enum Repr {
18 Arc(Arc<str>),
19 Inline(u23, [u8; INLINE_BYTES]),
20}
21
22#[allow(non_camel_case_types)]
23#[rustfmt::skip]
24#[derive(TryFromPrimitive, Clone, Copy)]
25#[repr(u8)]
26pub(crate) enum u23 {
27 _0, _1, _2, _3, _4, _5, _6, _7, _8, _9,
28 _10, _11, _12, _13, _14, _15, _16, _17, _18, _19,
29 _20, _21, _22, _23,
30}
31
32impl Deref for ImStr {
33 type Target = str;
34 #[inline]
35 fn deref(&self) -> &Self::Target {
36 match &self.0 {
37 Repr::Arc(inner) => inner,
38 Repr::Inline(len, bytes) => {
40 let slice = &bytes[..(*len as usize)];
41 unsafe { std::str::from_utf8_unchecked(slice) }
42 }
43 }
44 }
45}
46
47impl Borrow<[u8]> for ImStr {
48 fn borrow(&self) -> &[u8] {
49 self.as_str().as_bytes()
50 }
51}
52
53impl From<&str> for ImStr {
54 fn from(value: &str) -> Self {
55 let src = value.as_bytes();
56 if src.len() <= INLINE_BYTES {
57 let mut bytes = [0u8; INLINE_BYTES];
58 let slice = &mut bytes[..src.len()];
59 slice.copy_from_slice(src);
60 Self(Repr::Inline(u23::try_from(src.len() as u8).unwrap(), bytes))
61 } else {
62 Self(Repr::Arc(Arc::from(value)))
63 }
64 }
65}
66
67impl From<String> for ImStr {
68 #[inline]
69 fn from(value: String) -> Self {
70 value.as_str().into()
71 }
72}
73
74impl AsRef<str> for ImStr {
75 #[inline]
76 fn as_ref(&self) -> &str {
77 self.deref()
78 }
79}
80
81impl Display for ImStr {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 Display::fmt(self.deref(), f)
84 }
85}
86
87impl std::fmt::Debug for ImStr {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 std::fmt::Debug::fmt(self.deref(), f)
90 }
91}
92
93impl<T> PartialEq<T> for ImStr
94where
95 T: AsRef<str>,
96{
97 #[inline]
98 fn eq(&self, other: &T) -> bool {
99 self.deref() == other.as_ref()
100 }
101}
102
103impl<T> PartialOrd<T> for ImStr
104where
105 T: AsRef<str>,
106{
107 #[inline]
108 fn partial_cmp(&self, other: &T) -> Option<std::cmp::Ordering> {
109 self.deref().partial_cmp(other.as_ref())
110 }
111}
112
113impl PartialEq<ImStr> for str {
114 fn eq(&self, other: &ImStr) -> bool {
115 self == other.deref()
116 }
117}
118
119impl Eq for ImStr {}
120impl Ord for ImStr {
121 #[inline]
122 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
123 self.deref().cmp(other.deref())
124 }
125}
126
127impl std::hash::Hash for ImStr {
128 #[inline]
129 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
130 self.as_bytes().hash(state)
131 }
132}
133
134impl AsRef<Path> for ImStr {
135 fn as_ref(&self) -> &Path {
136 Path::new(self.as_str())
137 }
138}
139
140impl ImStr {
141 #[inline]
142 pub fn as_str(&self) -> &str {
143 self.deref()
144 }
145}
146
147const _: () = {
148 assertcp_eq!(
149 size_of::<ImStr>(),
150 size_of::<String>(),
151 "ImStr must be as large as String"
152 );
153 assertcp_eq!(
154 size_of::<Option<ImStr>>(),
155 size_of::<ImStr>(),
156 "An optional ImStr must be as large as ImStr"
157 );
158};