cheetah_string/
cheetah_string.rs

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
49/// # Safety Warning
50///
51/// This implementation uses `unsafe` code and may cause undefined behavior
52/// if the bytes are not valid UTF-8. Consider using `CheetahString::try_from_bytes()`
53/// for safe UTF-8 validation.
54///
55/// This implementation will be deprecated in a future version.
56impl From<&[u8]> for CheetahString {
57    #[inline]
58    fn from(b: &[u8]) -> Self {
59        // SAFETY: This is unsafe and may cause UB if bytes are not valid UTF-8.
60        // This will be deprecated in favor of try_from_bytes in the next version.
61        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
73/// # Safety Warning
74///
75/// This implementation uses `unsafe` code and may cause undefined behavior
76/// if the bytes are not valid UTF-8. Consider using `CheetahString::try_from_vec()`
77/// for safe UTF-8 validation.
78///
79/// This implementation will be deprecated in a future version.
80impl From<Vec<u8>> for CheetahString {
81    #[inline]
82    fn from(v: Vec<u8>) -> Self {
83        // SAFETY: This is unsafe and may cause UB if bytes are not valid UTF-8.
84        // This will be deprecated in favor of try_from_vec in the next version.
85        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    /// Allocates an owned [`CheetahString`] from a single character.
111    ///
112    /// # Example
113    /// ```rust
114    /// use cheetah_string::CheetahString;
115    /// let c: char = 'a';
116    /// let s: CheetahString = CheetahString::from(c);
117    /// assert_eq!("a", &s[..]);
118    /// ```
119    #[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                // SAFETY: Inline strings are always valid UTF-8
183                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                // SAFETY: ArcVecString should only be created from valid UTF-8 sources
198                unsafe { String::from_utf8_unchecked(s.to_vec()) }
199            }
200            #[cfg(feature = "bytes")]
201            CheetahString {
202                inner: InnerString::Bytes(b),
203            } => {
204                // SAFETY: Bytes variant should only be created from valid UTF-8 sources
205                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    /// Creates a `CheetahString` from a byte vector with UTF-8 validation.
279    ///
280    /// # Errors
281    ///
282    /// Returns an error if the bytes are not valid UTF-8.
283    ///
284    /// # Examples
285    ///
286    /// ```
287    /// use cheetah_string::CheetahString;
288    ///
289    /// let bytes = vec![104, 101, 108, 108, 111]; // "hello"
290    /// let s = CheetahString::try_from_vec(bytes).unwrap();
291    /// assert_eq!(s, "hello");
292    ///
293    /// let invalid = vec![0xFF, 0xFE];
294    /// assert!(CheetahString::try_from_vec(invalid).is_err());
295    /// ```
296    pub fn try_from_vec(v: Vec<u8>) -> Result<Self, Utf8Error> {
297        // Validate UTF-8
298        std::str::from_utf8(&v)?;
299        Ok(CheetahString {
300            inner: InnerString::ArcVecString(Arc::new(v)),
301        })
302    }
303
304    /// Creates a `CheetahString` from a byte slice with UTF-8 validation.
305    ///
306    /// # Errors
307    ///
308    /// Returns an error if the bytes are not valid UTF-8.
309    ///
310    /// # Examples
311    ///
312    /// ```
313    /// use cheetah_string::CheetahString;
314    ///
315    /// let bytes = b"hello";
316    /// let s = CheetahString::try_from_bytes(bytes).unwrap();
317    /// assert_eq!(s, "hello");
318    ///
319    /// let invalid = &[0xFF, 0xFE];
320    /// assert!(CheetahString::try_from_bytes(invalid).is_err());
321    /// ```
322    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            // Use inline storage for short strings
338            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            // Use Arc for long strings
348            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            // Use inline storage for short strings
358            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            // Use Arc<str> for long strings to avoid double allocation
368            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                // SAFETY: Inline strings are only created from valid UTF-8 sources.
394                // The data is always valid UTF-8 up to len bytes.
395                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                // SAFETY: ArcVecString is only created from validated UTF-8 sources.
402                // All constructors ensure this invariant is maintained.
403                unsafe { std::str::from_utf8_unchecked(s.as_ref()) }
404            }
405            #[cfg(feature = "bytes")]
406            InnerString::Bytes(b) => {
407                // SAFETY: Bytes variant is only created from validated UTF-8 sources.
408                // The from_bytes constructor ensures this invariant.
409                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    // Query methods - delegate to &str
454
455    /// Returns `true` if the string starts with the given pattern.
456    ///
457    /// # Examples
458    ///
459    /// ```
460    /// use cheetah_string::CheetahString;
461    ///
462    /// let s = CheetahString::from("hello world");
463    /// assert!(s.starts_with("hello"));
464    /// assert!(!s.starts_with("world"));
465    /// ```
466    #[inline]
467    pub fn starts_with<P: AsRef<str>>(&self, pat: P) -> bool {
468        self.as_str().starts_with(pat.as_ref())
469    }
470
471    /// Returns `true` if the string starts with the given character.
472    ///
473    /// # Examples
474    ///
475    /// ```
476    /// use cheetah_string::CheetahString;
477    ///
478    /// let s = CheetahString::from("hello world");
479    /// assert!(s.starts_with_char('h'));
480    /// assert!(!s.starts_with_char('w'));
481    /// ```
482    #[inline]
483    pub fn starts_with_char(&self, pat: char) -> bool {
484        self.as_str().starts_with(pat)
485    }
486
487    /// Returns `true` if the string ends with the given pattern.
488    ///
489    /// # Examples
490    ///
491    /// ```
492    /// use cheetah_string::CheetahString;
493    ///
494    /// let s = CheetahString::from("hello world");
495    /// assert!(s.ends_with("world"));
496    /// assert!(!s.ends_with("hello"));
497    /// ```
498    #[inline]
499    pub fn ends_with<P: AsRef<str>>(&self, pat: P) -> bool {
500        self.as_str().ends_with(pat.as_ref())
501    }
502
503    /// Returns `true` if the string ends with the given character.
504    ///
505    /// # Examples
506    ///
507    /// ```
508    /// use cheetah_string::CheetahString;
509    ///
510    /// let s = CheetahString::from("hello world");
511    /// assert!(s.ends_with_char('d'));
512    /// assert!(!s.ends_with_char('h'));
513    /// ```
514    #[inline]
515    pub fn ends_with_char(&self, pat: char) -> bool {
516        self.as_str().ends_with(pat)
517    }
518
519    /// Returns `true` if the string contains the given pattern.
520    ///
521    /// # Examples
522    ///
523    /// ```
524    /// use cheetah_string::CheetahString;
525    ///
526    /// let s = CheetahString::from("hello world");
527    /// assert!(s.contains("llo"));
528    /// assert!(!s.contains("xyz"));
529    /// ```
530    #[inline]
531    pub fn contains<P: AsRef<str>>(&self, pat: P) -> bool {
532        self.as_str().contains(pat.as_ref())
533    }
534
535    /// Returns `true` if the string contains the given character.
536    ///
537    /// # Examples
538    ///
539    /// ```
540    /// use cheetah_string::CheetahString;
541    ///
542    /// let s = CheetahString::from("hello world");
543    /// assert!(s.contains_char('o'));
544    /// assert!(!s.contains_char('x'));
545    /// ```
546    #[inline]
547    pub fn contains_char(&self, pat: char) -> bool {
548        self.as_str().contains(pat)
549    }
550
551    /// Returns the byte index of the first occurrence of the pattern, or `None` if not found.
552    ///
553    /// # Examples
554    ///
555    /// ```
556    /// use cheetah_string::CheetahString;
557    ///
558    /// let s = CheetahString::from("hello world");
559    /// assert_eq!(s.find("world"), Some(6));
560    /// assert_eq!(s.find("xyz"), None);
561    /// ```
562    #[inline]
563    pub fn find<P: AsRef<str>>(&self, pat: P) -> Option<usize> {
564        self.as_str().find(pat.as_ref())
565    }
566
567    /// Returns the byte index of the last occurrence of the pattern, or `None` if not found.
568    ///
569    /// # Examples
570    ///
571    /// ```
572    /// use cheetah_string::CheetahString;
573    ///
574    /// let s = CheetahString::from("hello hello");
575    /// assert_eq!(s.rfind("hello"), Some(6));
576    /// ```
577    #[inline]
578    pub fn rfind<P: AsRef<str>>(&self, pat: P) -> Option<usize> {
579        self.as_str().rfind(pat.as_ref())
580    }
581
582    /// Returns a string slice with leading and trailing whitespace removed.
583    ///
584    /// # Examples
585    ///
586    /// ```
587    /// use cheetah_string::CheetahString;
588    ///
589    /// let s = CheetahString::from("  hello  ");
590    /// assert_eq!(s.trim(), "hello");
591    /// ```
592    #[inline]
593    pub fn trim(&self) -> &str {
594        self.as_str().trim()
595    }
596
597    /// Returns a string slice with leading whitespace removed.
598    ///
599    /// # Examples
600    ///
601    /// ```
602    /// use cheetah_string::CheetahString;
603    ///
604    /// let s = CheetahString::from("  hello");
605    /// assert_eq!(s.trim_start(), "hello");
606    /// ```
607    #[inline]
608    pub fn trim_start(&self) -> &str {
609        self.as_str().trim_start()
610    }
611
612    /// Returns a string slice with trailing whitespace removed.
613    ///
614    /// # Examples
615    ///
616    /// ```
617    /// use cheetah_string::CheetahString;
618    ///
619    /// let s = CheetahString::from("hello  ");
620    /// assert_eq!(s.trim_end(), "hello");
621    /// ```
622    #[inline]
623    pub fn trim_end(&self) -> &str {
624        self.as_str().trim_end()
625    }
626
627    /// Splits the string by the given pattern.
628    ///
629    /// # Examples
630    ///
631    /// ```
632    /// use cheetah_string::CheetahString;
633    ///
634    /// let s = CheetahString::from("a,b,c");
635    /// let parts: Vec<&str> = s.split(",").collect();
636    /// assert_eq!(parts, vec!["a", "b", "c"]);
637    /// ```
638    #[inline]
639    pub fn split<'a>(&'a self, pat: &'a str) -> impl Iterator<Item = &'a str> {
640        self.as_str().split(pat)
641    }
642
643    /// Returns an iterator over the lines of the string.
644    ///
645    /// # Examples
646    ///
647    /// ```
648    /// use cheetah_string::CheetahString;
649    ///
650    /// let s = CheetahString::from("line1\nline2\nline3");
651    /// let lines: Vec<&str> = s.lines().collect();
652    /// assert_eq!(lines, vec!["line1", "line2", "line3"]);
653    /// ```
654    #[inline]
655    pub fn lines(&self) -> impl Iterator<Item = &str> {
656        self.as_str().lines()
657    }
658
659    /// Returns an iterator over the characters of the string.
660    ///
661    /// # Examples
662    ///
663    /// ```
664    /// use cheetah_string::CheetahString;
665    ///
666    /// let s = CheetahString::from("hello");
667    /// let chars: Vec<char> = s.chars().collect();
668    /// assert_eq!(chars, vec!['h', 'e', 'l', 'l', 'o']);
669    /// ```
670    #[inline]
671    pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
672        self.as_str().chars()
673    }
674
675    // Transformation methods - create new CheetahString
676
677    /// Returns a new `CheetahString` with all characters converted to uppercase.
678    ///
679    /// # Examples
680    ///
681    /// ```
682    /// use cheetah_string::CheetahString;
683    ///
684    /// let s = CheetahString::from("hello");
685    /// assert_eq!(s.to_uppercase(), "HELLO");
686    /// ```
687    #[inline]
688    pub fn to_uppercase(&self) -> CheetahString {
689        CheetahString::from_string(self.as_str().to_uppercase())
690    }
691
692    /// Returns a new `CheetahString` with all characters converted to lowercase.
693    ///
694    /// # Examples
695    ///
696    /// ```
697    /// use cheetah_string::CheetahString;
698    ///
699    /// let s = CheetahString::from("HELLO");
700    /// assert_eq!(s.to_lowercase(), "hello");
701    /// ```
702    #[inline]
703    pub fn to_lowercase(&self) -> CheetahString {
704        CheetahString::from_string(self.as_str().to_lowercase())
705    }
706
707    /// Replaces all occurrences of a pattern with another string.
708    ///
709    /// # Examples
710    ///
711    /// ```
712    /// use cheetah_string::CheetahString;
713    ///
714    /// let s = CheetahString::from("hello world");
715    /// assert_eq!(s.replace("world", "rust"), "hello rust");
716    /// ```
717    #[inline]
718    pub fn replace<P: AsRef<str>>(&self, from: P, to: &str) -> CheetahString {
719        CheetahString::from_string(self.as_str().replace(from.as_ref(), to))
720    }
721
722    /// Returns a new `CheetahString` with the specified range replaced.
723    ///
724    /// # Examples
725    ///
726    /// ```
727    /// use cheetah_string::CheetahString;
728    ///
729    /// let s = CheetahString::from("hello world");
730    /// assert_eq!(s.replacen("l", "L", 1), "heLlo world");
731    /// ```
732    #[inline]
733    pub fn replacen<P: AsRef<str>>(&self, from: P, to: &str, count: usize) -> CheetahString {
734        CheetahString::from_string(self.as_str().replacen(from.as_ref(), to, count))
735    }
736
737    /// Returns a substring as a new `CheetahString`.
738    ///
739    /// # Panics
740    ///
741    /// Panics if the indices are not on valid UTF-8 character boundaries.
742    ///
743    /// # Examples
744    ///
745    /// ```
746    /// use cheetah_string::CheetahString;
747    ///
748    /// let s = CheetahString::from("hello world");
749    /// assert_eq!(s.substring(0, 5), "hello");
750    /// assert_eq!(s.substring(6, 11), "world");
751    /// ```
752    #[inline]
753    pub fn substring(&self, start: usize, end: usize) -> CheetahString {
754        CheetahString::from_slice(&self.as_str()[start..end])
755    }
756
757    /// Repeats the string `n` times.
758    ///
759    /// # Examples
760    ///
761    /// ```
762    /// use cheetah_string::CheetahString;
763    ///
764    /// let s = CheetahString::from("abc");
765    /// assert_eq!(s.repeat(3), "abcabcabc");
766    /// ```
767    #[inline]
768    pub fn repeat(&self, n: usize) -> CheetahString {
769        CheetahString::from_string(self.as_str().repeat(n))
770    }
771
772    // Incremental building methods
773
774    /// Creates a new `CheetahString` with the specified capacity.
775    ///
776    /// The string will be able to hold at least `capacity` bytes without reallocating.
777    /// If `capacity` is less than or equal to the inline capacity (23 bytes),
778    /// an empty inline string is returned.
779    ///
780    /// # Examples
781    ///
782    /// ```
783    /// use cheetah_string::CheetahString;
784    ///
785    /// let mut s = CheetahString::with_capacity(100);
786    /// s.push_str("hello");
787    /// assert_eq!(s, "hello");
788    /// ```
789    #[inline]
790    pub fn with_capacity(capacity: usize) -> Self {
791        if capacity <= INLINE_CAPACITY {
792            CheetahString::empty()
793        } else {
794            CheetahString::from_string(String::with_capacity(capacity))
795        }
796    }
797
798    /// Appends a string slice to the end of this `CheetahString`.
799    ///
800    /// This method is optimized for incremental building and will:
801    /// - Mutate inline storage when possible
802    /// - Mutate unique Arc<String> in-place when available
803    /// - Only allocate when necessary
804    ///
805    /// # Examples
806    ///
807    /// ```
808    /// use cheetah_string::CheetahString;
809    ///
810    /// let mut s = CheetahString::from("Hello");
811    /// s.push_str(" ");
812    /// s.push_str("World");
813    /// assert_eq!(s, "Hello World");
814    /// ```
815    #[inline]
816    pub fn push_str(&mut self, string: &str) {
817        *self += string;
818    }
819
820    /// Reserves capacity for at least `additional` more bytes.
821    ///
822    /// This method will modify the internal representation if needed to ensure
823    /// that the string can hold at least `additional` more bytes without reallocating.
824    ///
825    /// # Examples
826    ///
827    /// ```
828    /// use cheetah_string::CheetahString;
829    ///
830    /// let mut s = CheetahString::from("hello");
831    /// s.reserve(100);
832    /// s.push_str(" world");
833    /// ```
834    #[inline]
835    pub fn reserve(&mut self, additional: usize) {
836        let new_len = self.len() + additional;
837
838        // If it still fits inline, nothing to do
839        if new_len <= INLINE_CAPACITY {
840            return;
841        }
842
843        match &mut self.inner {
844            InnerString::Inline { .. } => {
845                // Convert inline to Arc<String> with capacity
846                let mut s = String::with_capacity(new_len);
847                s.push_str(self.as_str());
848                *self = CheetahString {
849                    inner: InnerString::ArcString(Arc::new(s)),
850                };
851            }
852            InnerString::ArcString(arc) if Arc::strong_count(arc) == 1 => {
853                // Reserve in the unique Arc<String>
854                if let Some(s) = Arc::get_mut(arc) {
855                    s.reserve(additional);
856                }
857            }
858            InnerString::StaticStr(_) | InnerString::ArcStr(_) => {
859                // Convert to Arc<String> with capacity
860                let mut s = String::with_capacity(new_len);
861                s.push_str(self.as_str());
862                *self = CheetahString {
863                    inner: InnerString::ArcString(Arc::new(s)),
864                };
865            }
866            _ => {
867                // For shared Arc or other types, convert if needed
868                if Arc::strong_count(match &self.inner {
869                    InnerString::ArcString(arc) => arc,
870                    _ => return,
871                }) > 1
872                {
873                    let mut s = String::with_capacity(new_len);
874                    s.push_str(self.as_str());
875                    *self = CheetahString {
876                        inner: InnerString::ArcString(Arc::new(s)),
877                    };
878                }
879            }
880        }
881    }
882}
883
884impl PartialEq for CheetahString {
885    #[inline]
886    fn eq(&self, other: &Self) -> bool {
887        self.as_str() == other.as_str()
888    }
889}
890
891impl PartialEq<str> for CheetahString {
892    #[inline]
893    fn eq(&self, other: &str) -> bool {
894        self.as_str() == other
895    }
896}
897
898impl PartialEq<String> for CheetahString {
899    #[inline]
900    fn eq(&self, other: &String) -> bool {
901        self.as_str() == other.as_str()
902    }
903}
904
905impl PartialEq<Vec<u8>> for CheetahString {
906    #[inline]
907    fn eq(&self, other: &Vec<u8>) -> bool {
908        self.as_bytes() == other.as_slice()
909    }
910}
911
912impl<'a> PartialEq<&'a str> for CheetahString {
913    #[inline]
914    fn eq(&self, other: &&'a str) -> bool {
915        self.as_str() == *other
916    }
917}
918
919impl PartialEq<CheetahString> for str {
920    #[inline]
921    fn eq(&self, other: &CheetahString) -> bool {
922        self == other.as_str()
923    }
924}
925
926impl PartialEq<CheetahString> for String {
927    #[inline]
928    fn eq(&self, other: &CheetahString) -> bool {
929        self.as_str() == other.as_str()
930    }
931}
932
933impl PartialEq<CheetahString> for &str {
934    #[inline]
935    fn eq(&self, other: &CheetahString) -> bool {
936        *self == other.as_str()
937    }
938}
939
940impl Eq for CheetahString {}
941
942impl PartialOrd for CheetahString {
943    #[inline]
944    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
945        Some(self.cmp(other))
946    }
947}
948
949impl Ord for CheetahString {
950    #[inline]
951    fn cmp(&self, other: &Self) -> Ordering {
952        self.as_str().cmp(other.as_str())
953    }
954}
955
956impl Hash for CheetahString {
957    #[inline]
958    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
959        self.as_str().hash(state);
960    }
961}
962
963impl Display for CheetahString {
964    #[inline]
965    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
966        self.as_str().fmt(f)
967    }
968}
969
970impl std::fmt::Debug for CheetahString {
971    #[inline]
972    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
973        fmt::Debug::fmt(self.as_str(), f)
974    }
975}
976
977impl Borrow<str> for CheetahString {
978    #[inline]
979    fn borrow(&self) -> &str {
980        self.as_str()
981    }
982}
983
984// Add trait implementations for string concatenation
985
986impl std::ops::Add<&str> for CheetahString {
987    type Output = CheetahString;
988
989    /// Concatenates a `CheetahString` with a string slice.
990    ///
991    /// # Examples
992    ///
993    /// ```
994    /// use cheetah_string::CheetahString;
995    ///
996    /// let s = CheetahString::from("Hello");
997    /// let result = s + " World";
998    /// assert_eq!(result, "Hello World");
999    /// ```
1000    #[inline]
1001    fn add(self, rhs: &str) -> Self::Output {
1002        let total_len = self.len() + rhs.len();
1003
1004        // Fast path: result fits in inline storage
1005        if total_len <= INLINE_CAPACITY {
1006            let mut data = [0u8; INLINE_CAPACITY];
1007            let self_bytes = self.as_bytes();
1008            data[..self_bytes.len()].copy_from_slice(self_bytes);
1009            data[self_bytes.len()..total_len].copy_from_slice(rhs.as_bytes());
1010            return CheetahString {
1011                inner: InnerString::Inline {
1012                    len: total_len as u8,
1013                    data,
1014                },
1015            };
1016        }
1017
1018        // Slow path: allocate for long result
1019        let mut result = String::with_capacity(total_len);
1020        result.push_str(self.as_str());
1021        result.push_str(rhs);
1022        CheetahString::from_string(result)
1023    }
1024}
1025
1026impl std::ops::Add<&CheetahString> for CheetahString {
1027    type Output = CheetahString;
1028
1029    /// Concatenates two `CheetahString` values.
1030    ///
1031    /// # Examples
1032    ///
1033    /// ```
1034    /// use cheetah_string::CheetahString;
1035    ///
1036    /// let s1 = CheetahString::from("Hello");
1037    /// let s2 = CheetahString::from(" World");
1038    /// let result = s1 + &s2;
1039    /// assert_eq!(result, "Hello World");
1040    /// ```
1041    #[inline]
1042    fn add(self, rhs: &CheetahString) -> Self::Output {
1043        let total_len = self.len() + rhs.len();
1044
1045        // Fast path: result fits in inline storage
1046        if total_len <= INLINE_CAPACITY {
1047            let mut data = [0u8; INLINE_CAPACITY];
1048            let self_bytes = self.as_bytes();
1049            data[..self_bytes.len()].copy_from_slice(self_bytes);
1050            data[self_bytes.len()..total_len].copy_from_slice(rhs.as_bytes());
1051            return CheetahString {
1052                inner: InnerString::Inline {
1053                    len: total_len as u8,
1054                    data,
1055                },
1056            };
1057        }
1058
1059        // Slow path: allocate for long result
1060        let mut result = String::with_capacity(total_len);
1061        result.push_str(self.as_str());
1062        result.push_str(rhs.as_str());
1063        CheetahString::from_string(result)
1064    }
1065}
1066
1067impl std::ops::Add<String> for CheetahString {
1068    type Output = CheetahString;
1069
1070    /// Concatenates a `CheetahString` with a `String`.
1071    ///
1072    /// # Examples
1073    ///
1074    /// ```
1075    /// use cheetah_string::CheetahString;
1076    ///
1077    /// let s = CheetahString::from("Hello");
1078    /// let result = s + String::from(" World");
1079    /// assert_eq!(result, "Hello World");
1080    /// ```
1081    #[inline]
1082    fn add(self, rhs: String) -> Self::Output {
1083        let total_len = self.len() + rhs.len();
1084
1085        // Fast path: result fits in inline storage
1086        if total_len <= INLINE_CAPACITY {
1087            let mut data = [0u8; INLINE_CAPACITY];
1088            let self_bytes = self.as_bytes();
1089            data[..self_bytes.len()].copy_from_slice(self_bytes);
1090            data[self_bytes.len()..total_len].copy_from_slice(rhs.as_bytes());
1091            return CheetahString {
1092                inner: InnerString::Inline {
1093                    len: total_len as u8,
1094                    data,
1095                },
1096            };
1097        }
1098
1099        // Slow path: allocate for long result
1100        let mut result = String::with_capacity(total_len);
1101        result.push_str(self.as_str());
1102        result.push_str(&rhs);
1103        CheetahString::from_string(result)
1104    }
1105}
1106
1107impl std::ops::AddAssign<&str> for CheetahString {
1108    /// Appends a string slice to a `CheetahString`.
1109    ///
1110    /// # Examples
1111    ///
1112    /// ```
1113    /// use cheetah_string::CheetahString;
1114    ///
1115    /// let mut s = CheetahString::from("Hello");
1116    /// s += " World";
1117    /// assert_eq!(s, "Hello World");
1118    /// ```
1119    #[inline]
1120    fn add_assign(&mut self, rhs: &str) {
1121        let total_len = self.len() + rhs.len();
1122
1123        match &mut self.inner {
1124            // Fast path 1: Both self and result fit in inline storage
1125            InnerString::Inline { len, data } if total_len <= INLINE_CAPACITY => {
1126                // Mutate inline buffer directly
1127                data[*len as usize..total_len].copy_from_slice(rhs.as_bytes());
1128                *len = total_len as u8;
1129                return;
1130            }
1131            // Fast path 2: Self is unique Arc<String>, mutate in-place
1132            InnerString::ArcString(arc) if Arc::strong_count(arc) == 1 => {
1133                // SAFETY: strong_count == 1 guarantees exclusive access
1134                if let Some(s) = Arc::get_mut(arc) {
1135                    s.push_str(rhs);
1136                    return;
1137                }
1138            }
1139            _ => {}
1140        }
1141
1142        // Slow path: allocate new string
1143        let mut result = String::with_capacity(total_len);
1144        result.push_str(self.as_str());
1145        result.push_str(rhs);
1146        *self = CheetahString::from_string(result);
1147    }
1148}
1149
1150impl std::ops::AddAssign<&CheetahString> for CheetahString {
1151    /// Appends a `CheetahString` to another `CheetahString`.
1152    ///
1153    /// # Examples
1154    ///
1155    /// ```
1156    /// use cheetah_string::CheetahString;
1157    ///
1158    /// let mut s1 = CheetahString::from("Hello");
1159    /// let s2 = CheetahString::from(" World");
1160    /// s1 += &s2;
1161    /// assert_eq!(s1, "Hello World");
1162    /// ```
1163    #[inline]
1164    fn add_assign(&mut self, rhs: &CheetahString) {
1165        let total_len = self.len() + rhs.len();
1166
1167        match &mut self.inner {
1168            // Fast path 1: Both self and result fit in inline storage
1169            InnerString::Inline { len, data } if total_len <= INLINE_CAPACITY => {
1170                // Mutate inline buffer directly
1171                data[*len as usize..total_len].copy_from_slice(rhs.as_bytes());
1172                *len = total_len as u8;
1173                return;
1174            }
1175            // Fast path 2: Self is unique Arc<String>, mutate in-place
1176            InnerString::ArcString(arc) if Arc::strong_count(arc) == 1 => {
1177                // SAFETY: strong_count == 1 guarantees exclusive access
1178                if let Some(s) = Arc::get_mut(arc) {
1179                    s.push_str(rhs.as_str());
1180                    return;
1181                }
1182            }
1183            _ => {}
1184        }
1185
1186        // Slow path: allocate new string
1187        let mut result = String::with_capacity(total_len);
1188        result.push_str(self.as_str());
1189        result.push_str(rhs.as_str());
1190        *self = CheetahString::from_string(result);
1191    }
1192}
1193
1194/// Maximum capacity for inline string storage (23 bytes + 1 byte for length = 24 bytes total)
1195const INLINE_CAPACITY: usize = 23;
1196
1197/// The `InnerString` enum represents different types of string storage.
1198///
1199/// This enum uses Small String Optimization (SSO) to avoid heap allocations for short strings.
1200///
1201/// Variants:
1202///
1203/// * `Inline` - Inline storage for strings <= 23 bytes (zero heap allocations).
1204/// * `StaticStr(&'static str)` - A static string slice (zero heap allocations).
1205/// * `ArcStr(Arc<str>)` - A reference-counted string slice (single heap allocation, optimized).
1206/// * `ArcString(Arc<String>)` - A reference-counted string (for backwards compatibility).
1207/// * `ArcVecString(Arc<Vec<u8>>)` - A reference-counted byte vector.
1208/// * `Bytes(bytes::Bytes)` - A byte buffer (available when the "bytes" feature is enabled).
1209#[derive(Clone)]
1210pub(super) enum InnerString {
1211    /// Inline storage for short strings (up to 23 bytes).
1212    /// Stores the length and data directly without heap allocation.
1213    Inline {
1214        len: u8,
1215        data: [u8; INLINE_CAPACITY],
1216    },
1217    /// Static string slice with 'static lifetime.
1218    StaticStr(&'static str),
1219    /// Reference-counted string slice (single heap allocation).
1220    /// Preferred over ArcString for long strings created from owned data.
1221    ArcStr(Arc<str>),
1222    /// Reference-counted heap-allocated string.
1223    /// Kept for backwards compatibility and when Arc<String> is explicitly provided.
1224    ArcString(Arc<String>),
1225    /// Reference-counted heap-allocated byte vector.
1226    ArcVecString(Arc<Vec<u8>>),
1227    /// Bytes type integration (requires "bytes" feature).
1228    #[cfg(feature = "bytes")]
1229    Bytes(bytes::Bytes),
1230}