1use core::fmt;
2use core::str::Utf8Error;
3use std::borrow::{Borrow, Cow};
4use std::cmp::Ordering;
5use std::fmt::Display;
6use std::hash::Hash;
7use std::ops::Deref;
8use std::str::FromStr;
9use std::sync::Arc;
10
11#[derive(Clone)]
12#[repr(transparent)]
13pub struct CheetahString {
14 pub(super) inner: InnerString,
15}
16
17impl Default for CheetahString {
18 fn default() -> Self {
19 CheetahString {
20 inner: InnerString::Inline {
21 len: 0,
22 data: [0; INLINE_CAPACITY],
23 },
24 }
25 }
26}
27
28impl From<String> for CheetahString {
29 #[inline]
30 fn from(s: String) -> Self {
31 CheetahString::from_string(s)
32 }
33}
34
35impl From<Arc<String>> for CheetahString {
36 #[inline]
37 fn from(s: Arc<String>) -> Self {
38 CheetahString::from_arc_string(s)
39 }
40}
41
42impl<'a> From<&'a str> for CheetahString {
43 #[inline]
44 fn from(s: &'a str) -> Self {
45 CheetahString::from_slice(s)
46 }
47}
48
49impl From<&[u8]> for CheetahString {
57 #[inline]
58 fn from(b: &[u8]) -> Self {
59 CheetahString::from_slice(unsafe { std::str::from_utf8_unchecked(b) })
62 }
63}
64
65impl FromStr for CheetahString {
66 type Err = std::string::ParseError;
67 #[inline]
68 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 Ok(CheetahString::from_slice(s))
70 }
71}
72
73impl From<Vec<u8>> for CheetahString {
81 #[inline]
82 fn from(v: Vec<u8>) -> Self {
83 CheetahString::from_slice(unsafe { std::str::from_utf8_unchecked(&v) })
86 }
87}
88
89impl From<Cow<'static, str>> for CheetahString {
90 #[inline]
91 fn from(cow: Cow<'static, str>) -> Self {
92 match cow {
93 Cow::Borrowed(s) => CheetahString::from_static_str(s),
94 Cow::Owned(s) => CheetahString::from_string(s),
95 }
96 }
97}
98
99impl From<Cow<'_, String>> for CheetahString {
100 #[inline]
101 fn from(cow: Cow<'_, String>) -> Self {
102 match cow {
103 Cow::Borrowed(s) => CheetahString::from_slice(s),
104 Cow::Owned(s) => CheetahString::from_string(s),
105 }
106 }
107}
108
109impl From<char> for CheetahString {
110 #[inline]
120 fn from(c: char) -> Self {
121 CheetahString::from_string(c.to_string())
122 }
123}
124
125impl<'a> FromIterator<&'a char> for CheetahString {
126 #[inline]
127 fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> CheetahString {
128 let mut buf = String::new();
129 buf.extend(iter);
130 CheetahString::from_string(buf)
131 }
132}
133
134impl<'a> FromIterator<&'a str> for CheetahString {
135 fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> CheetahString {
136 let mut buf = String::new();
137 buf.extend(iter);
138 CheetahString::from_string(buf)
139 }
140}
141
142impl FromIterator<String> for CheetahString {
143 #[inline]
144 fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
145 let mut buf = String::new();
146 buf.extend(iter);
147 CheetahString::from_string(buf)
148 }
149}
150
151impl<'a> FromIterator<&'a String> for CheetahString {
152 #[inline]
153 fn from_iter<T: IntoIterator<Item = &'a String>>(iter: T) -> Self {
154 let mut buf = String::new();
155 buf.extend(iter.into_iter().map(|s| s.as_str()));
156 CheetahString::from_string(buf)
157 }
158}
159
160#[cfg(feature = "bytes")]
161impl From<bytes::Bytes> for CheetahString {
162 #[inline]
163 fn from(b: bytes::Bytes) -> Self {
164 CheetahString::from_bytes(b)
165 }
166}
167
168impl From<&CheetahString> for CheetahString {
169 #[inline]
170 fn from(s: &CheetahString) -> Self {
171 s.clone()
172 }
173}
174
175impl From<CheetahString> for String {
176 #[inline]
177 fn from(s: CheetahString) -> Self {
178 match s {
179 CheetahString {
180 inner: InnerString::Inline { len, data },
181 } => {
182 unsafe { String::from_utf8_unchecked(data[..len as usize].to_vec()) }
184 }
185 CheetahString {
186 inner: InnerString::StaticStr(s),
187 } => s.to_string(),
188 CheetahString {
189 inner: InnerString::ArcStr(s),
190 } => s.to_string(),
191 CheetahString {
192 inner: InnerString::ArcString(s),
193 } => s.as_ref().clone(),
194 CheetahString {
195 inner: InnerString::ArcVecString(s),
196 } => {
197 unsafe { String::from_utf8_unchecked(s.to_vec()) }
199 }
200 #[cfg(feature = "bytes")]
201 CheetahString {
202 inner: InnerString::Bytes(b),
203 } => {
204 unsafe { String::from_utf8_unchecked(b.to_vec()) }
206 }
207 }
208 }
209}
210
211impl Deref for CheetahString {
212 type Target = str;
213
214 #[inline]
215 fn deref(&self) -> &Self::Target {
216 self.as_str()
217 }
218}
219
220impl AsRef<str> for CheetahString {
221 #[inline]
222 fn as_ref(&self) -> &str {
223 self.as_str()
224 }
225}
226
227impl AsRef<[u8]> for CheetahString {
228 #[inline]
229 fn as_ref(&self) -> &[u8] {
230 self.as_bytes()
231 }
232}
233
234impl AsRef<CheetahString> for CheetahString {
235 #[inline]
236 fn as_ref(&self) -> &CheetahString {
237 self
238 }
239}
240
241impl From<&String> for CheetahString {
242 #[inline]
243 fn from(s: &String) -> Self {
244 CheetahString::from_slice(s)
245 }
246}
247
248impl CheetahString {
249 #[inline]
250 pub const fn empty() -> Self {
251 CheetahString {
252 inner: InnerString::Inline {
253 len: 0,
254 data: [0; INLINE_CAPACITY],
255 },
256 }
257 }
258
259 #[inline]
260 pub fn new() -> Self {
261 CheetahString::default()
262 }
263
264 #[inline]
265 pub const fn from_static_str(s: &'static str) -> Self {
266 CheetahString {
267 inner: InnerString::StaticStr(s),
268 }
269 }
270
271 #[inline]
272 pub fn from_vec(s: Vec<u8>) -> Self {
273 CheetahString {
274 inner: InnerString::ArcVecString(Arc::new(s)),
275 }
276 }
277
278 pub fn try_from_vec(v: Vec<u8>) -> Result<Self, Utf8Error> {
297 std::str::from_utf8(&v)?;
299 Ok(CheetahString {
300 inner: InnerString::ArcVecString(Arc::new(v)),
301 })
302 }
303
304 pub fn try_from_bytes(b: &[u8]) -> Result<Self, Utf8Error> {
323 let s = std::str::from_utf8(b)?;
324 Ok(CheetahString::from_slice(s))
325 }
326
327 #[inline]
328 pub fn from_arc_vec(s: Arc<Vec<u8>>) -> Self {
329 CheetahString {
330 inner: InnerString::ArcVecString(s),
331 }
332 }
333
334 #[inline]
335 pub fn from_slice(s: &str) -> Self {
336 if s.len() <= INLINE_CAPACITY {
337 let mut data = [0u8; INLINE_CAPACITY];
339 data[..s.len()].copy_from_slice(s.as_bytes());
340 CheetahString {
341 inner: InnerString::Inline {
342 len: s.len() as u8,
343 data,
344 },
345 }
346 } else {
347 CheetahString {
349 inner: InnerString::ArcString(Arc::new(s.to_owned())),
350 }
351 }
352 }
353
354 #[inline]
355 pub fn from_string(s: String) -> Self {
356 if s.len() <= INLINE_CAPACITY {
357 let mut data = [0u8; INLINE_CAPACITY];
359 data[..s.len()].copy_from_slice(s.as_bytes());
360 CheetahString {
361 inner: InnerString::Inline {
362 len: s.len() as u8,
363 data,
364 },
365 }
366 } else {
367 let arc_str: Arc<str> = s.into_boxed_str().into();
369 CheetahString {
370 inner: InnerString::ArcStr(arc_str),
371 }
372 }
373 }
374 #[inline]
375 pub fn from_arc_string(s: Arc<String>) -> Self {
376 CheetahString {
377 inner: InnerString::ArcString(s),
378 }
379 }
380
381 #[inline]
382 #[cfg(feature = "bytes")]
383 pub fn from_bytes(b: bytes::Bytes) -> Self {
384 CheetahString {
385 inner: InnerString::Bytes(b),
386 }
387 }
388
389 #[inline]
390 pub fn as_str(&self) -> &str {
391 match &self.inner {
392 InnerString::Inline { len, data } => {
393 unsafe { std::str::from_utf8_unchecked(&data[..*len as usize]) }
396 }
397 InnerString::StaticStr(s) => s,
398 InnerString::ArcStr(s) => s.as_ref(),
399 InnerString::ArcString(s) => s.as_str(),
400 InnerString::ArcVecString(s) => {
401 unsafe { std::str::from_utf8_unchecked(s.as_ref()) }
404 }
405 #[cfg(feature = "bytes")]
406 InnerString::Bytes(b) => {
407 unsafe { std::str::from_utf8_unchecked(b.as_ref()) }
410 }
411 }
412 }
413
414 #[inline]
415 pub fn as_bytes(&self) -> &[u8] {
416 match &self.inner {
417 InnerString::Inline { len, data } => &data[..*len as usize],
418 InnerString::StaticStr(s) => s.as_bytes(),
419 InnerString::ArcStr(s) => s.as_bytes(),
420 InnerString::ArcString(s) => s.as_bytes(),
421 InnerString::ArcVecString(s) => s.as_ref(),
422 #[cfg(feature = "bytes")]
423 InnerString::Bytes(b) => b.as_ref(),
424 }
425 }
426
427 #[inline]
428 pub fn len(&self) -> usize {
429 match &self.inner {
430 InnerString::Inline { len, .. } => *len as usize,
431 InnerString::StaticStr(s) => s.len(),
432 InnerString::ArcStr(s) => s.len(),
433 InnerString::ArcString(s) => s.len(),
434 InnerString::ArcVecString(s) => s.len(),
435 #[cfg(feature = "bytes")]
436 InnerString::Bytes(b) => b.len(),
437 }
438 }
439
440 #[inline]
441 pub fn is_empty(&self) -> bool {
442 match &self.inner {
443 InnerString::Inline { len, .. } => *len == 0,
444 InnerString::StaticStr(s) => s.is_empty(),
445 InnerString::ArcStr(s) => s.is_empty(),
446 InnerString::ArcString(s) => s.is_empty(),
447 InnerString::ArcVecString(s) => s.is_empty(),
448 #[cfg(feature = "bytes")]
449 InnerString::Bytes(b) => b.is_empty(),
450 }
451 }
452
453 #[inline]
471 pub fn starts_with<P: StrPattern>(&self, pat: P) -> bool {
472 match pat.as_str_pattern() {
473 StrPatternImpl::Char(c) => self.as_str().starts_with(c),
474 StrPatternImpl::Str(s) => {
475 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
476 {
477 crate::simd::starts_with_bytes(self.as_bytes(), s.as_bytes())
478 }
479 #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
480 {
481 self.as_str().starts_with(s)
482 }
483 }
484 }
485 }
486
487 #[inline]
499 pub fn starts_with_char(&self, pat: char) -> bool {
500 self.as_str().starts_with(pat)
501 }
502
503 #[inline]
519 pub fn ends_with<P: StrPattern>(&self, pat: P) -> bool {
520 match pat.as_str_pattern() {
521 StrPatternImpl::Char(c) => self.as_str().ends_with(c),
522 StrPatternImpl::Str(s) => {
523 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
524 {
525 crate::simd::ends_with_bytes(self.as_bytes(), s.as_bytes())
526 }
527 #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
528 {
529 self.as_str().ends_with(s)
530 }
531 }
532 }
533 }
534
535 #[inline]
547 pub fn ends_with_char(&self, pat: char) -> bool {
548 self.as_str().ends_with(pat)
549 }
550
551 #[inline]
567 pub fn contains<P: StrPattern>(&self, pat: P) -> bool {
568 match pat.as_str_pattern() {
569 StrPatternImpl::Char(c) => self.as_str().contains(c),
570 StrPatternImpl::Str(s) => {
571 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
572 {
573 crate::simd::find_bytes(self.as_bytes(), s.as_bytes()).is_some()
574 }
575 #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
576 {
577 self.as_str().contains(s)
578 }
579 }
580 }
581 }
582
583 #[inline]
595 pub fn contains_char(&self, pat: char) -> bool {
596 self.as_str().contains(pat)
597 }
598
599 #[inline]
614 pub fn find<P: AsRef<str>>(&self, pat: P) -> Option<usize> {
615 let pat = pat.as_ref();
616 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
617 {
618 crate::simd::find_bytes(self.as_bytes(), pat.as_bytes())
619 }
620 #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
621 {
622 self.as_str().find(pat)
623 }
624 }
625
626 #[inline]
637 pub fn rfind<P: AsRef<str>>(&self, pat: P) -> Option<usize> {
638 self.as_str().rfind(pat.as_ref())
639 }
640
641 #[inline]
652 pub fn trim(&self) -> &str {
653 self.as_str().trim()
654 }
655
656 #[inline]
667 pub fn trim_start(&self) -> &str {
668 self.as_str().trim_start()
669 }
670
671 #[inline]
682 pub fn trim_end(&self) -> &str {
683 self.as_str().trim_end()
684 }
685
686 #[inline]
700 pub fn split<'a, P>(&'a self, pat: P) -> SplitWrapper<'a>
701 where
702 P: SplitPattern<'a>,
703 {
704 pat.split_str(self.as_str())
705 }
706
707 #[inline]
719 pub fn lines(&self) -> impl Iterator<Item = &str> {
720 self.as_str().lines()
721 }
722
723 #[inline]
737 pub fn chars(&self) -> std::str::Chars<'_> {
738 self.as_str().chars()
739 }
740
741 #[inline]
754 pub fn to_uppercase(&self) -> CheetahString {
755 CheetahString::from_string(self.as_str().to_uppercase())
756 }
757
758 #[inline]
769 pub fn to_lowercase(&self) -> CheetahString {
770 CheetahString::from_string(self.as_str().to_lowercase())
771 }
772
773 #[inline]
784 pub fn replace<P: AsRef<str>>(&self, from: P, to: &str) -> CheetahString {
785 CheetahString::from_string(self.as_str().replace(from.as_ref(), to))
786 }
787
788 #[inline]
799 pub fn replacen<P: AsRef<str>>(&self, from: P, to: &str, count: usize) -> CheetahString {
800 CheetahString::from_string(self.as_str().replacen(from.as_ref(), to, count))
801 }
802
803 #[inline]
819 pub fn substring(&self, start: usize, end: usize) -> CheetahString {
820 CheetahString::from_slice(&self.as_str()[start..end])
821 }
822
823 #[inline]
834 pub fn repeat(&self, n: usize) -> CheetahString {
835 CheetahString::from_string(self.as_str().repeat(n))
836 }
837
838 #[inline]
856 pub fn with_capacity(capacity: usize) -> Self {
857 if capacity <= INLINE_CAPACITY {
858 CheetahString::empty()
859 } else {
860 CheetahString::from_string(String::with_capacity(capacity))
861 }
862 }
863
864 #[inline]
882 pub fn push_str(&mut self, string: &str) {
883 *self += string;
884 }
885
886 #[inline]
901 pub fn reserve(&mut self, additional: usize) {
902 let new_len = self.len() + additional;
903
904 if new_len <= INLINE_CAPACITY {
906 return;
907 }
908
909 match &mut self.inner {
910 InnerString::Inline { .. } => {
911 let mut s = String::with_capacity(new_len);
913 s.push_str(self.as_str());
914 *self = CheetahString {
915 inner: InnerString::ArcString(Arc::new(s)),
916 };
917 }
918 InnerString::ArcString(arc) if Arc::strong_count(arc) == 1 => {
919 if let Some(s) = Arc::get_mut(arc) {
921 s.reserve(additional);
922 }
923 }
924 InnerString::StaticStr(_) | InnerString::ArcStr(_) => {
925 let mut s = String::with_capacity(new_len);
927 s.push_str(self.as_str());
928 *self = CheetahString {
929 inner: InnerString::ArcString(Arc::new(s)),
930 };
931 }
932 _ => {
933 if Arc::strong_count(match &self.inner {
935 InnerString::ArcString(arc) => arc,
936 _ => return,
937 }) > 1
938 {
939 let mut s = String::with_capacity(new_len);
940 s.push_str(self.as_str());
941 *self = CheetahString {
942 inner: InnerString::ArcString(Arc::new(s)),
943 };
944 }
945 }
946 }
947 }
948}
949
950impl PartialEq for CheetahString {
951 #[inline]
952 fn eq(&self, other: &Self) -> bool {
953 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
954 {
955 crate::simd::eq_bytes(self.as_bytes(), other.as_bytes())
956 }
957 #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
958 {
959 self.as_str() == other.as_str()
960 }
961 }
962}
963
964impl PartialEq<str> for CheetahString {
965 #[inline]
966 fn eq(&self, other: &str) -> bool {
967 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
968 {
969 crate::simd::eq_bytes(self.as_bytes(), other.as_bytes())
970 }
971 #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
972 {
973 self.as_str() == other
974 }
975 }
976}
977
978impl PartialEq<String> for CheetahString {
979 #[inline]
980 fn eq(&self, other: &String) -> bool {
981 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
982 {
983 crate::simd::eq_bytes(self.as_bytes(), other.as_bytes())
984 }
985 #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
986 {
987 self.as_str() == other.as_str()
988 }
989 }
990}
991
992impl PartialEq<Vec<u8>> for CheetahString {
993 #[inline]
994 fn eq(&self, other: &Vec<u8>) -> bool {
995 self.as_bytes() == other.as_slice()
996 }
997}
998
999impl<'a> PartialEq<&'a str> for CheetahString {
1000 #[inline]
1001 fn eq(&self, other: &&'a str) -> bool {
1002 self.as_str() == *other
1003 }
1004}
1005
1006impl PartialEq<CheetahString> for str {
1007 #[inline]
1008 fn eq(&self, other: &CheetahString) -> bool {
1009 self == other.as_str()
1010 }
1011}
1012
1013impl PartialEq<CheetahString> for String {
1014 #[inline]
1015 fn eq(&self, other: &CheetahString) -> bool {
1016 self.as_str() == other.as_str()
1017 }
1018}
1019
1020impl PartialEq<CheetahString> for &str {
1021 #[inline]
1022 fn eq(&self, other: &CheetahString) -> bool {
1023 *self == other.as_str()
1024 }
1025}
1026
1027impl Eq for CheetahString {}
1028
1029impl PartialOrd for CheetahString {
1030 #[inline]
1031 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1032 Some(self.cmp(other))
1033 }
1034}
1035
1036impl Ord for CheetahString {
1037 #[inline]
1038 fn cmp(&self, other: &Self) -> Ordering {
1039 self.as_str().cmp(other.as_str())
1040 }
1041}
1042
1043impl Hash for CheetahString {
1044 #[inline]
1045 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1046 self.as_str().hash(state);
1047 }
1048}
1049
1050impl Display for CheetahString {
1051 #[inline]
1052 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1053 self.as_str().fmt(f)
1054 }
1055}
1056
1057impl std::fmt::Debug for CheetahString {
1058 #[inline]
1059 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1060 fmt::Debug::fmt(self.as_str(), f)
1061 }
1062}
1063
1064impl Borrow<str> for CheetahString {
1065 #[inline]
1066 fn borrow(&self) -> &str {
1067 self.as_str()
1068 }
1069}
1070
1071impl std::ops::Add<&str> for CheetahString {
1074 type Output = CheetahString;
1075
1076 #[inline]
1088 fn add(self, rhs: &str) -> Self::Output {
1089 let total_len = self.len() + rhs.len();
1090
1091 if total_len <= INLINE_CAPACITY {
1093 let mut data = [0u8; INLINE_CAPACITY];
1094 let self_bytes = self.as_bytes();
1095 data[..self_bytes.len()].copy_from_slice(self_bytes);
1096 data[self_bytes.len()..total_len].copy_from_slice(rhs.as_bytes());
1097 return CheetahString {
1098 inner: InnerString::Inline {
1099 len: total_len as u8,
1100 data,
1101 },
1102 };
1103 }
1104
1105 let mut result = String::with_capacity(total_len);
1107 result.push_str(self.as_str());
1108 result.push_str(rhs);
1109 CheetahString::from_string(result)
1110 }
1111}
1112
1113impl std::ops::Add<&CheetahString> for CheetahString {
1114 type Output = CheetahString;
1115
1116 #[inline]
1129 fn add(self, rhs: &CheetahString) -> Self::Output {
1130 let total_len = self.len() + rhs.len();
1131
1132 if total_len <= INLINE_CAPACITY {
1134 let mut data = [0u8; INLINE_CAPACITY];
1135 let self_bytes = self.as_bytes();
1136 data[..self_bytes.len()].copy_from_slice(self_bytes);
1137 data[self_bytes.len()..total_len].copy_from_slice(rhs.as_bytes());
1138 return CheetahString {
1139 inner: InnerString::Inline {
1140 len: total_len as u8,
1141 data,
1142 },
1143 };
1144 }
1145
1146 let mut result = String::with_capacity(total_len);
1148 result.push_str(self.as_str());
1149 result.push_str(rhs.as_str());
1150 CheetahString::from_string(result)
1151 }
1152}
1153
1154impl std::ops::Add<String> for CheetahString {
1155 type Output = CheetahString;
1156
1157 #[inline]
1169 fn add(self, rhs: String) -> Self::Output {
1170 let total_len = self.len() + rhs.len();
1171
1172 if total_len <= INLINE_CAPACITY {
1174 let mut data = [0u8; INLINE_CAPACITY];
1175 let self_bytes = self.as_bytes();
1176 data[..self_bytes.len()].copy_from_slice(self_bytes);
1177 data[self_bytes.len()..total_len].copy_from_slice(rhs.as_bytes());
1178 return CheetahString {
1179 inner: InnerString::Inline {
1180 len: total_len as u8,
1181 data,
1182 },
1183 };
1184 }
1185
1186 let mut result = String::with_capacity(total_len);
1188 result.push_str(self.as_str());
1189 result.push_str(&rhs);
1190 CheetahString::from_string(result)
1191 }
1192}
1193
1194impl std::ops::AddAssign<&str> for CheetahString {
1195 #[inline]
1207 fn add_assign(&mut self, rhs: &str) {
1208 let total_len = self.len() + rhs.len();
1209
1210 match &mut self.inner {
1211 InnerString::Inline { len, data } if total_len <= INLINE_CAPACITY => {
1213 data[*len as usize..total_len].copy_from_slice(rhs.as_bytes());
1215 *len = total_len as u8;
1216 return;
1217 }
1218 InnerString::ArcString(arc) if Arc::strong_count(arc) == 1 => {
1220 if let Some(s) = Arc::get_mut(arc) {
1222 s.push_str(rhs);
1223 return;
1224 }
1225 }
1226 _ => {}
1227 }
1228
1229 let mut result = String::with_capacity(total_len);
1231 result.push_str(self.as_str());
1232 result.push_str(rhs);
1233 *self = CheetahString::from_string(result);
1234 }
1235}
1236
1237impl std::ops::AddAssign<&CheetahString> for CheetahString {
1238 #[inline]
1251 fn add_assign(&mut self, rhs: &CheetahString) {
1252 let total_len = self.len() + rhs.len();
1253
1254 match &mut self.inner {
1255 InnerString::Inline { len, data } if total_len <= INLINE_CAPACITY => {
1257 data[*len as usize..total_len].copy_from_slice(rhs.as_bytes());
1259 *len = total_len as u8;
1260 return;
1261 }
1262 InnerString::ArcString(arc) if Arc::strong_count(arc) == 1 => {
1264 if let Some(s) = Arc::get_mut(arc) {
1266 s.push_str(rhs.as_str());
1267 return;
1268 }
1269 }
1270 _ => {}
1271 }
1272
1273 let mut result = String::with_capacity(total_len);
1275 result.push_str(self.as_str());
1276 result.push_str(rhs.as_str());
1277 *self = CheetahString::from_string(result);
1278 }
1279}
1280
1281const INLINE_CAPACITY: usize = 23;
1283
1284#[derive(Clone)]
1297pub(super) enum InnerString {
1298 Inline {
1301 len: u8,
1302 data: [u8; INLINE_CAPACITY],
1303 },
1304 StaticStr(&'static str),
1306 ArcStr(Arc<str>),
1309 ArcString(Arc<String>),
1312 ArcVecString(Arc<Vec<u8>>),
1314 #[cfg(feature = "bytes")]
1316 Bytes(bytes::Bytes),
1317}
1318
1319mod private {
1321 pub trait Sealed {}
1322 impl Sealed for char {}
1323 impl Sealed for &str {}
1324 impl Sealed for &String {}
1325
1326 pub trait SplitSealed {}
1327 impl SplitSealed for char {}
1328 impl SplitSealed for &str {}
1329}
1330
1331pub trait StrPattern: private::Sealed {
1333 #[doc(hidden)]
1334 fn as_str_pattern(&self) -> StrPatternImpl<'_>;
1335}
1336
1337#[doc(hidden)]
1338pub enum StrPatternImpl<'a> {
1339 Char(char),
1340 Str(&'a str),
1341}
1342
1343impl StrPattern for char {
1344 fn as_str_pattern(&self) -> StrPatternImpl<'_> {
1345 StrPatternImpl::Char(*self)
1346 }
1347}
1348
1349impl StrPattern for &str {
1350 fn as_str_pattern(&self) -> StrPatternImpl<'_> {
1351 StrPatternImpl::Str(self)
1352 }
1353}
1354
1355impl StrPattern for &String {
1356 fn as_str_pattern(&self) -> StrPatternImpl<'_> {
1357 StrPatternImpl::Str(self.as_str())
1358 }
1359}
1360
1361pub trait SplitPattern<'a>: private::SplitSealed {
1363 #[doc(hidden)]
1364 fn split_str(self, s: &'a str) -> SplitWrapper<'a>;
1365}
1366
1367impl SplitPattern<'_> for char {
1368 fn split_str(self, s: &str) -> SplitWrapper<'_> {
1369 SplitWrapper::Char(s.split(self))
1370 }
1371}
1372
1373impl<'a> SplitPattern<'a> for &'a str {
1374 fn split_str(self, s: &'a str) -> SplitWrapper<'a> {
1375 let empty_pattern_state = if self.is_empty() {
1376 Some(EmptyPatternState {
1377 chars: s.char_indices(),
1378 original: s,
1379 started: false,
1380 })
1381 } else {
1382 None
1383 };
1384
1385 SplitWrapper::Str(SplitStr {
1386 string: s,
1387 pattern: self,
1388 finished: false,
1389 empty_pattern_state,
1390 })
1391 }
1392}
1393
1394pub struct SplitStr<'a> {
1396 string: &'a str,
1397 pattern: &'a str,
1398 finished: bool,
1399 empty_pattern_state: Option<EmptyPatternState<'a>>,
1401}
1402
1403#[derive(Clone)]
1404struct EmptyPatternState<'a> {
1405 chars: std::str::CharIndices<'a>,
1406 original: &'a str,
1407 started: bool,
1408}
1409
1410impl<'a> Iterator for SplitStr<'a> {
1411 type Item = &'a str;
1412
1413 fn next(&mut self) -> Option<Self::Item> {
1414 if self.finished {
1415 return None;
1416 }
1417
1418 if self.pattern.is_empty() {
1420 if let Some(ref mut state) = self.empty_pattern_state {
1421 if !state.started {
1422 state.started = true;
1423 return Some("");
1425 }
1426
1427 match state.chars.next() {
1428 Some((pos, ch)) => {
1429 let char_end = pos + ch.len_utf8();
1430 let result = &state.original[pos..char_end];
1431 Some(result)
1432 }
1433 None => {
1434 self.finished = true;
1435 Some("")
1437 }
1438 }
1439 } else {
1440 unreachable!("empty_pattern_state should be Some for empty pattern")
1441 }
1442 } else {
1443 match self.string.find(self.pattern) {
1445 Some(pos) => {
1446 let result = &self.string[..pos];
1447 self.string = &self.string[pos + self.pattern.len()..];
1448 Some(result)
1449 }
1450 None => {
1451 self.finished = true;
1452 Some(self.string)
1453 }
1454 }
1455 }
1456 }
1457}
1458
1459pub enum SplitWrapper<'a> {
1461 #[doc(hidden)]
1462 Char(std::str::Split<'a, char>),
1463 #[doc(hidden)]
1464 Str(SplitStr<'a>),
1465}
1466
1467impl<'a> Iterator for SplitWrapper<'a> {
1468 type Item = &'a str;
1469
1470 fn next(&mut self) -> Option<Self::Item> {
1471 match self {
1472 SplitWrapper::Char(iter) => iter.next(),
1473 SplitWrapper::Str(iter) => iter.next(),
1474 }
1475 }
1476}
1477
1478impl<'a> DoubleEndedIterator for SplitWrapper<'a> {
1479 fn next_back(&mut self) -> Option<Self::Item> {
1480 match self {
1481 SplitWrapper::Char(iter) => iter.next_back(),
1482 SplitWrapper::Str(_) => {
1483 panic!("split with string pattern does not support reverse iteration")
1486 }
1487 }
1488 }
1489}