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