Skip to main content

cheetah_string/
cheetah_string.rs

1use alloc::borrow::Cow;
2use alloc::string::{ParseError, String, ToString};
3use alloc::sync::Arc;
4use alloc::vec::Vec;
5use core::borrow::Borrow;
6use core::cmp::Ordering;
7use core::fmt::{self, Display};
8use core::hash::{Hash, Hasher};
9use core::ops::{Add, AddAssign, Deref};
10use core::str::{self, FromStr, Utf8Error};
11
12#[derive(Clone)]
13#[repr(transparent)]
14pub struct CheetahString {
15    pub(super) inner: InnerString,
16}
17
18impl Default for CheetahString {
19    fn default() -> Self {
20        CheetahString {
21            inner: InnerString::Inline {
22                len: 0,
23                data: [0; INLINE_CAPACITY],
24            },
25        }
26    }
27}
28
29impl From<String> for CheetahString {
30    #[inline]
31    fn from(s: String) -> Self {
32        CheetahString::from_string(s)
33    }
34}
35
36impl From<Arc<String>> for CheetahString {
37    #[inline]
38    fn from(s: Arc<String>) -> Self {
39        CheetahString::from_arc_string(s)
40    }
41}
42
43impl<'a> From<&'a str> for CheetahString {
44    #[inline]
45    fn from(s: &'a str) -> Self {
46        CheetahString::from_slice(s)
47    }
48}
49
50impl<'a> TryFrom<&'a [u8]> for CheetahString {
51    type Error = Utf8Error;
52
53    #[inline]
54    fn try_from(b: &'a [u8]) -> Result<Self, Self::Error> {
55        CheetahString::try_from_bytes(b)
56    }
57}
58
59impl FromStr for CheetahString {
60    type Err = ParseError;
61    #[inline]
62    fn from_str(s: &str) -> Result<Self, Self::Err> {
63        Ok(CheetahString::from_slice(s))
64    }
65}
66
67impl TryFrom<Vec<u8>> for CheetahString {
68    type Error = Utf8Error;
69
70    #[inline]
71    fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
72        CheetahString::try_from_vec(v)
73    }
74}
75
76impl From<Cow<'static, str>> for CheetahString {
77    #[inline]
78    fn from(cow: Cow<'static, str>) -> Self {
79        match cow {
80            Cow::Borrowed(s) => CheetahString::from_static_str(s),
81            Cow::Owned(s) => CheetahString::from_string(s),
82        }
83    }
84}
85
86impl From<Cow<'_, String>> for CheetahString {
87    #[inline]
88    fn from(cow: Cow<'_, String>) -> Self {
89        match cow {
90            Cow::Borrowed(s) => CheetahString::from_slice(s),
91            Cow::Owned(s) => CheetahString::from_string(s),
92        }
93    }
94}
95
96impl From<char> for CheetahString {
97    /// Allocates an owned [`CheetahString`] from a single character.
98    ///
99    /// # Example
100    /// ```rust
101    /// use cheetah_string::CheetahString;
102    /// let c: char = 'a';
103    /// let s: CheetahString = CheetahString::from(c);
104    /// assert_eq!("a", &s[..]);
105    /// ```
106    #[inline]
107    fn from(c: char) -> Self {
108        CheetahString::from_string(c.to_string())
109    }
110}
111
112impl<'a> FromIterator<&'a char> for CheetahString {
113    #[inline]
114    fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> CheetahString {
115        let mut buf = String::new();
116        buf.extend(iter);
117        CheetahString::from_string(buf)
118    }
119}
120
121impl<'a> FromIterator<&'a str> for CheetahString {
122    fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> CheetahString {
123        let mut buf = String::new();
124        buf.extend(iter);
125        CheetahString::from_string(buf)
126    }
127}
128
129impl FromIterator<String> for CheetahString {
130    #[inline]
131    fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
132        let mut buf = String::new();
133        buf.extend(iter);
134        CheetahString::from_string(buf)
135    }
136}
137
138impl<'a> FromIterator<&'a String> for CheetahString {
139    #[inline]
140    fn from_iter<T: IntoIterator<Item = &'a String>>(iter: T) -> Self {
141        let mut buf = String::new();
142        buf.extend(iter.into_iter().map(|s| s.as_str()));
143        CheetahString::from_string(buf)
144    }
145}
146
147#[cfg(feature = "bytes")]
148impl TryFrom<bytes::Bytes> for CheetahString {
149    type Error = Utf8Error;
150
151    #[inline]
152    fn try_from(b: bytes::Bytes) -> Result<Self, Self::Error> {
153        CheetahString::try_from_bytes_buf(b)
154    }
155}
156
157impl From<&CheetahString> for CheetahString {
158    #[inline]
159    fn from(s: &CheetahString) -> Self {
160        s.clone()
161    }
162}
163
164impl From<CheetahString> for String {
165    #[inline]
166    fn from(s: CheetahString) -> Self {
167        match s {
168            CheetahString {
169                inner: InnerString::Inline { len, data },
170            } => {
171                // SAFETY: Inline strings are always valid UTF-8
172                unsafe { String::from_utf8_unchecked(data[..len as usize].to_vec()) }
173            }
174            CheetahString {
175                inner: InnerString::Static(s),
176            } => s.to_string(),
177            CheetahString {
178                inner: InnerString::Shared(s),
179            } => s.to_string(),
180            CheetahString {
181                inner: InnerString::Owned(s),
182            } => s,
183        }
184    }
185}
186
187impl Deref for CheetahString {
188    type Target = str;
189
190    #[inline]
191    fn deref(&self) -> &Self::Target {
192        self.as_str()
193    }
194}
195
196impl AsRef<str> for CheetahString {
197    #[inline]
198    fn as_ref(&self) -> &str {
199        self.as_str()
200    }
201}
202
203impl AsRef<[u8]> for CheetahString {
204    #[inline]
205    fn as_ref(&self) -> &[u8] {
206        self.as_bytes()
207    }
208}
209
210impl AsRef<CheetahString> for CheetahString {
211    #[inline]
212    fn as_ref(&self) -> &CheetahString {
213        self
214    }
215}
216
217impl From<&String> for CheetahString {
218    #[inline]
219    fn from(s: &String) -> Self {
220        CheetahString::from_slice(s)
221    }
222}
223
224impl CheetahString {
225    #[inline]
226    pub const fn empty() -> Self {
227        CheetahString {
228            inner: InnerString::Inline {
229                len: 0,
230                data: [0; INLINE_CAPACITY],
231            },
232        }
233    }
234
235    #[inline]
236    pub fn new() -> Self {
237        CheetahString::default()
238    }
239
240    #[inline]
241    pub const fn from_static_str(s: &'static str) -> Self {
242        CheetahString {
243            inner: InnerString::Static(s),
244        }
245    }
246
247    #[deprecated(
248        since = "1.1.0",
249        note = "use try_from_vec for checked construction or from_utf8_unchecked_vec for an explicit unsafe constructor"
250    )]
251    pub fn from_vec(s: Vec<u8>) -> Self {
252        CheetahString::try_from_vec(s).expect(
253            "CheetahString::from_vec requires valid UTF-8; use try_from_vec for fallible construction",
254        )
255    }
256
257    /// Creates a `CheetahString` from a byte vector without validating UTF-8.
258    ///
259    /// # Safety
260    ///
261    /// The caller must guarantee that `s` contains valid UTF-8 for the entire
262    /// lifetime of the returned `CheetahString`.
263    #[inline]
264    pub unsafe fn from_utf8_unchecked_vec(s: Vec<u8>) -> Self {
265        CheetahString::from_validated_vec_unchecked(s)
266    }
267
268    #[inline]
269    fn from_validated_vec_unchecked(s: Vec<u8>) -> Self {
270        if s.len() <= INLINE_CAPACITY {
271            let mut data = [0u8; INLINE_CAPACITY];
272            data[..s.len()].copy_from_slice(&s);
273            CheetahString {
274                inner: InnerString::Inline {
275                    len: s.len() as u8,
276                    data,
277                },
278            }
279        } else {
280            // SAFETY: Callers validate UTF-8 before reaching this helper.
281            CheetahString::from_builder_string(unsafe { String::from_utf8_unchecked(s) })
282        }
283    }
284
285    /// Creates a `CheetahString` from a byte vector with UTF-8 validation.
286    ///
287    /// # Errors
288    ///
289    /// Returns an error if the bytes are not valid UTF-8.
290    ///
291    /// # Examples
292    ///
293    /// ```
294    /// use cheetah_string::CheetahString;
295    ///
296    /// let bytes = vec![104, 101, 108, 108, 111]; // "hello"
297    /// let s = CheetahString::try_from_vec(bytes).unwrap();
298    /// assert_eq!(s, "hello");
299    ///
300    /// let invalid = vec![0xFF, 0xFE];
301    /// assert!(CheetahString::try_from_vec(invalid).is_err());
302    /// ```
303    pub fn try_from_vec(v: Vec<u8>) -> Result<Self, Utf8Error> {
304        str::from_utf8(&v)?;
305        Ok(CheetahString::from_validated_vec_unchecked(v))
306    }
307
308    /// Creates a `CheetahString` from a byte slice with UTF-8 validation.
309    ///
310    /// # Errors
311    ///
312    /// Returns an error if the bytes are not valid UTF-8.
313    ///
314    /// # Examples
315    ///
316    /// ```
317    /// use cheetah_string::CheetahString;
318    ///
319    /// let bytes = b"hello";
320    /// let s = CheetahString::try_from_bytes(bytes).unwrap();
321    /// assert_eq!(s, "hello");
322    ///
323    /// let invalid = &[0xFF, 0xFE];
324    /// assert!(CheetahString::try_from_bytes(invalid).is_err());
325    /// ```
326    pub fn try_from_bytes(b: &[u8]) -> Result<Self, Utf8Error> {
327        let s = str::from_utf8(b)?;
328        Ok(CheetahString::from_slice(s))
329    }
330
331    /// Creates a `CheetahString` from a byte slice without validating UTF-8.
332    ///
333    /// # Safety
334    ///
335    /// The caller must guarantee that `b` contains valid UTF-8.
336    #[inline]
337    pub unsafe fn from_utf8_unchecked_bytes(b: &[u8]) -> Self {
338        // SAFETY: The caller guarantees that `b` contains valid UTF-8.
339        CheetahString::from_slice(unsafe { str::from_utf8_unchecked(b) })
340    }
341
342    /// Creates a `CheetahString` from a shared byte vector with UTF-8 validation.
343    ///
344    /// # Errors
345    ///
346    /// Returns an error if the bytes are not valid UTF-8.
347    #[inline]
348    pub fn try_from_arc_vec(s: Arc<Vec<u8>>) -> Result<Self, Utf8Error> {
349        match Arc::try_unwrap(s) {
350            Ok(v) => CheetahString::try_from_vec(v),
351            Err(s) => {
352                let s = str::from_utf8(s.as_slice())?;
353                Ok(CheetahString::from_slice(s))
354            }
355        }
356    }
357
358    #[deprecated(
359        since = "1.1.0",
360        note = "use try_from_arc_vec for checked construction or from_utf8_unchecked_arc_vec for an explicit unsafe constructor"
361    )]
362    #[inline]
363    pub fn from_arc_vec(s: Arc<Vec<u8>>) -> Self {
364        CheetahString::try_from_arc_vec(s).expect(
365            "CheetahString::from_arc_vec requires valid UTF-8; use try_from_arc_vec for fallible construction",
366        )
367    }
368
369    /// Creates a `CheetahString` from a shared byte vector without validating UTF-8.
370    ///
371    /// # Safety
372    ///
373    /// The caller must guarantee that `s` contains valid UTF-8.
374    #[inline]
375    pub unsafe fn from_utf8_unchecked_arc_vec(s: Arc<Vec<u8>>) -> Self {
376        CheetahString::from_validated_arc_vec_unchecked(s)
377    }
378
379    #[inline]
380    fn from_validated_arc_vec_unchecked(s: Arc<Vec<u8>>) -> Self {
381        match Arc::try_unwrap(s) {
382            Ok(v) => CheetahString::from_validated_vec_unchecked(v),
383            Err(s) => {
384                // SAFETY: Callers validate UTF-8 before reaching this helper.
385                unsafe { CheetahString::from_utf8_unchecked_bytes(s.as_slice()) }
386            }
387        }
388    }
389
390    #[inline]
391    pub fn from_slice(s: &str) -> Self {
392        if s.len() <= INLINE_CAPACITY {
393            // Use inline storage for short strings
394            let mut data = [0u8; INLINE_CAPACITY];
395            data[..s.len()].copy_from_slice(s.as_bytes());
396            CheetahString {
397                inner: InnerString::Inline {
398                    len: s.len() as u8,
399                    data,
400                },
401            }
402        } else {
403            // Use Arc<str> for long borrowed strings to avoid the extra String header.
404            let arc_str: Arc<str> = Arc::from(s);
405            CheetahString {
406                inner: InnerString::Shared(arc_str),
407            }
408        }
409    }
410
411    #[inline]
412    pub fn from_string(s: String) -> Self {
413        CheetahString::from_string_shared(s)
414    }
415
416    /// Creates a `CheetahString` from an owned `String` while preserving
417    /// ownership and spare capacity for later mutation.
418    ///
419    /// This constructor is intended for builder-style paths that will continue
420    /// appending to the string. It keeps long strings in owned storage instead
421    /// of converting them to shared storage.
422    #[inline]
423    pub fn from_string_owned(s: String) -> Self {
424        CheetahString::from_builder_string(s)
425    }
426
427    /// Creates a `CheetahString` from an owned `String` using shared storage
428    /// for long immutable strings.
429    ///
430    /// This is the same storage policy used by `from_string` in the 1.x line.
431    /// Use `from_string_owned` when spare capacity should be preserved for
432    /// later mutation.
433    #[inline]
434    pub fn from_string_shared(s: String) -> Self {
435        if s.len() <= INLINE_CAPACITY {
436            // Use inline storage for short strings
437            let mut data = [0u8; INLINE_CAPACITY];
438            data[..s.len()].copy_from_slice(s.as_bytes());
439            CheetahString {
440                inner: InnerString::Inline {
441                    len: s.len() as u8,
442                    data,
443                },
444            }
445        } else {
446            // Use Arc<str> for long strings to avoid double allocation
447            let arc_str: Arc<str> = s.into_boxed_str().into();
448            CheetahString {
449                inner: InnerString::Shared(arc_str),
450            }
451        }
452    }
453
454    #[inline]
455    fn from_builder_string(s: String) -> Self {
456        if s.len() <= INLINE_CAPACITY && s.capacity() <= INLINE_CAPACITY {
457            let mut data = [0u8; INLINE_CAPACITY];
458            data[..s.len()].copy_from_slice(s.as_bytes());
459            CheetahString {
460                inner: InnerString::Inline {
461                    len: s.len() as u8,
462                    data,
463                },
464            }
465        } else {
466            CheetahString {
467                inner: InnerString::Owned(s),
468            }
469        }
470    }
471
472    #[inline]
473    pub fn from_arc_string(s: Arc<String>) -> Self {
474        match Arc::try_unwrap(s) {
475            Ok(s) => CheetahString::from_builder_string(s),
476            Err(s) => CheetahString::from_slice(s.as_str()),
477        }
478    }
479
480    #[inline]
481    #[cfg(feature = "bytes")]
482    #[deprecated(
483        since = "1.1.0",
484        note = "use try_from_bytes_buf for checked construction or from_utf8_unchecked_bytes_buf for an explicit unsafe constructor"
485    )]
486    pub fn from_bytes(b: bytes::Bytes) -> Self {
487        CheetahString::try_from_bytes_buf(b).expect(
488            "CheetahString::from_bytes requires valid UTF-8; use try_from_bytes_buf for fallible construction",
489        )
490    }
491
492    #[inline]
493    #[cfg(feature = "bytes")]
494    pub fn try_from_bytes_buf(b: bytes::Bytes) -> Result<Self, Utf8Error> {
495        str::from_utf8(b.as_ref())?;
496        Ok(CheetahString::from_validated_bytes_unchecked(b))
497    }
498
499    /// Creates a `CheetahString` from `bytes::Bytes` without validating UTF-8.
500    ///
501    /// # Safety
502    ///
503    /// The caller must guarantee that `b` contains valid UTF-8.
504    #[inline]
505    #[cfg(feature = "bytes")]
506    pub unsafe fn from_utf8_unchecked_bytes_buf(b: bytes::Bytes) -> Self {
507        CheetahString::from_validated_bytes_unchecked(b)
508    }
509
510    #[inline]
511    #[cfg(feature = "bytes")]
512    fn from_validated_bytes_unchecked(b: bytes::Bytes) -> Self {
513        // SAFETY: Callers validate UTF-8 before reaching this helper.
514        unsafe { CheetahString::from_utf8_unchecked_bytes(b.as_ref()) }
515    }
516
517    #[inline]
518    pub fn as_str(&self) -> &str {
519        match &self.inner {
520            InnerString::Inline { len, data } => {
521                // SAFETY: Inline strings are only created from valid UTF-8 sources.
522                // The data is always valid UTF-8 up to len bytes.
523                unsafe { str::from_utf8_unchecked(&data[..*len as usize]) }
524            }
525            InnerString::Static(s) => s,
526            InnerString::Shared(s) => s.as_ref(),
527            InnerString::Owned(s) => s.as_str(),
528        }
529    }
530
531    #[inline]
532    pub fn as_bytes(&self) -> &[u8] {
533        match &self.inner {
534            InnerString::Inline { len, data } => &data[..*len as usize],
535            InnerString::Static(s) => s.as_bytes(),
536            InnerString::Shared(s) => s.as_bytes(),
537            InnerString::Owned(s) => s.as_bytes(),
538        }
539    }
540
541    #[inline]
542    pub fn len(&self) -> usize {
543        match &self.inner {
544            InnerString::Inline { len, .. } => *len as usize,
545            InnerString::Static(s) => s.len(),
546            InnerString::Shared(s) => s.len(),
547            InnerString::Owned(s) => s.len(),
548        }
549    }
550
551    #[inline]
552    pub fn is_empty(&self) -> bool {
553        match &self.inner {
554            InnerString::Inline { len, .. } => *len == 0,
555            InnerString::Static(s) => s.is_empty(),
556            InnerString::Shared(s) => s.is_empty(),
557            InnerString::Owned(s) => s.is_empty(),
558        }
559    }
560
561    // Query methods - delegate to &str
562
563    /// Returns `true` if the string starts with the given pattern.
564    ///
565    /// When the `simd` feature is enabled, this method uses SIMD instructions
566    /// for improved performance on longer patterns.
567    ///
568    /// # Examples
569    ///
570    /// ```
571    /// use cheetah_string::CheetahString;
572    ///
573    /// let s = CheetahString::from("hello world");
574    /// assert!(s.starts_with("hello"));
575    /// assert!(!s.starts_with("world"));
576    /// assert!(s.starts_with('h'));
577    /// ```
578    #[inline]
579    pub fn starts_with<P: StrPattern>(&self, pat: P) -> bool {
580        match pat.as_str_pattern() {
581            StrPatternImpl::Char(c) => self.as_str().starts_with(c),
582            StrPatternImpl::Str(s) => {
583                #[cfg(all(feature = "simd", target_arch = "x86_64"))]
584                {
585                    if s.len() >= crate::simd::SIMD_THRESHOLD {
586                        return crate::simd::starts_with_bytes(self.as_bytes(), s.as_bytes());
587                    }
588                }
589
590                self.as_str().starts_with(s)
591            }
592        }
593    }
594
595    /// Returns `true` if the string starts with the given character.
596    ///
597    /// # Examples
598    ///
599    /// ```
600    /// use cheetah_string::CheetahString;
601    ///
602    /// let s = CheetahString::from("hello world");
603    /// assert!(s.starts_with_char('h'));
604    /// assert!(!s.starts_with_char('w'));
605    /// ```
606    #[inline]
607    pub fn starts_with_char(&self, pat: char) -> bool {
608        self.as_str().starts_with(pat)
609    }
610
611    /// Returns `true` if the string ends with the given pattern.
612    ///
613    /// When the `simd` feature is enabled, this method uses SIMD instructions
614    /// for improved performance on longer patterns.
615    ///
616    /// # Examples
617    ///
618    /// ```
619    /// use cheetah_string::CheetahString;
620    ///
621    /// let s = CheetahString::from("hello world");
622    /// assert!(s.ends_with("world"));
623    /// assert!(!s.ends_with("hello"));
624    /// assert!(s.ends_with('d'));
625    /// ```
626    #[inline]
627    pub fn ends_with<P: StrPattern>(&self, pat: P) -> bool {
628        match pat.as_str_pattern() {
629            StrPatternImpl::Char(c) => self.as_str().ends_with(c),
630            StrPatternImpl::Str(s) => {
631                #[cfg(all(feature = "simd", target_arch = "x86_64"))]
632                {
633                    if s.len() >= crate::simd::SIMD_THRESHOLD {
634                        return crate::simd::ends_with_bytes(self.as_bytes(), s.as_bytes());
635                    }
636                }
637
638                self.as_str().ends_with(s)
639            }
640        }
641    }
642
643    /// Returns `true` if the string ends with the given character.
644    ///
645    /// # Examples
646    ///
647    /// ```
648    /// use cheetah_string::CheetahString;
649    ///
650    /// let s = CheetahString::from("hello world");
651    /// assert!(s.ends_with_char('d'));
652    /// assert!(!s.ends_with_char('h'));
653    /// ```
654    #[inline]
655    pub fn ends_with_char(&self, pat: char) -> bool {
656        self.as_str().ends_with(pat)
657    }
658
659    /// Returns `true` if the string contains the given pattern.
660    ///
661    /// When the `simd` feature is enabled, this method uses SIMD instructions
662    /// for improved performance on longer patterns.
663    ///
664    /// # Examples
665    ///
666    /// ```
667    /// use cheetah_string::CheetahString;
668    ///
669    /// let s = CheetahString::from("hello world");
670    /// assert!(s.contains("llo"));
671    /// assert!(!s.contains("xyz"));
672    /// assert!(s.contains('o'));
673    /// ```
674    #[inline]
675    pub fn contains<P: StrPattern>(&self, pat: P) -> bool {
676        match pat.as_str_pattern() {
677            StrPatternImpl::Char(c) => self.as_str().contains(c),
678            StrPatternImpl::Str(s) => {
679                crate::search::find_bytes(self.as_bytes(), s.as_bytes()).is_some()
680            }
681        }
682    }
683
684    /// Returns `true` if the string contains the given character.
685    ///
686    /// # Examples
687    ///
688    /// ```
689    /// use cheetah_string::CheetahString;
690    ///
691    /// let s = CheetahString::from("hello world");
692    /// assert!(s.contains_char('o'));
693    /// assert!(!s.contains_char('x'));
694    /// ```
695    #[inline]
696    pub fn contains_char(&self, pat: char) -> bool {
697        self.as_str().contains(pat)
698    }
699
700    /// Returns the byte index of the first occurrence of the pattern, or `None` if not found.
701    ///
702    /// When the `simd` feature is enabled, this method uses SIMD instructions
703    /// for improved performance on longer patterns.
704    ///
705    /// # Examples
706    ///
707    /// ```
708    /// use cheetah_string::CheetahString;
709    ///
710    /// let s = CheetahString::from("hello world");
711    /// assert_eq!(s.find("world"), Some(6));
712    /// assert_eq!(s.find("xyz"), None);
713    /// ```
714    #[inline]
715    pub fn find<P: AsRef<str>>(&self, pat: P) -> Option<usize> {
716        let pat = pat.as_ref();
717        crate::search::find_bytes(self.as_bytes(), pat.as_bytes())
718    }
719
720    /// Returns the byte index of the last occurrence of the pattern, or `None` if not found.
721    ///
722    /// # Examples
723    ///
724    /// ```
725    /// use cheetah_string::CheetahString;
726    ///
727    /// let s = CheetahString::from("hello hello");
728    /// assert_eq!(s.rfind("hello"), Some(6));
729    /// ```
730    #[inline]
731    pub fn rfind<P: AsRef<str>>(&self, pat: P) -> Option<usize> {
732        crate::search::rfind_bytes(self.as_bytes(), pat.as_ref().as_bytes())
733    }
734
735    /// Returns a string slice with leading and trailing whitespace removed.
736    ///
737    /// # Examples
738    ///
739    /// ```
740    /// use cheetah_string::CheetahString;
741    ///
742    /// let s = CheetahString::from("  hello  ");
743    /// assert_eq!(s.trim(), "hello");
744    /// ```
745    #[inline]
746    pub fn trim(&self) -> &str {
747        self.as_str().trim()
748    }
749
750    /// Returns a string slice with leading whitespace removed.
751    ///
752    /// # Examples
753    ///
754    /// ```
755    /// use cheetah_string::CheetahString;
756    ///
757    /// let s = CheetahString::from("  hello");
758    /// assert_eq!(s.trim_start(), "hello");
759    /// ```
760    #[inline]
761    pub fn trim_start(&self) -> &str {
762        self.as_str().trim_start()
763    }
764
765    /// Returns a string slice with trailing whitespace removed.
766    ///
767    /// # Examples
768    ///
769    /// ```
770    /// use cheetah_string::CheetahString;
771    ///
772    /// let s = CheetahString::from("hello  ");
773    /// assert_eq!(s.trim_end(), "hello");
774    /// ```
775    #[inline]
776    pub fn trim_end(&self) -> &str {
777        self.as_str().trim_end()
778    }
779
780    /// Splits the string by the given pattern.
781    ///
782    /// # Examples
783    ///
784    /// ```
785    /// use cheetah_string::CheetahString;
786    ///
787    /// let s = CheetahString::from("a,b,c");
788    /// let parts: Vec<&str> = s.split(",").collect();
789    /// assert_eq!(parts, vec!["a", "b", "c"]);
790    /// let parts2: Vec<&str> = s.split(',').collect();
791    /// assert_eq!(parts2, vec!["a", "b", "c"]);
792    /// ```
793    #[inline]
794    pub fn split<'a, P>(&'a self, pat: P) -> SplitWrapper<'a>
795    where
796        P: SplitPattern<'a>,
797    {
798        pat.split_str(self.as_str())
799    }
800
801    /// Returns an iterator over the lines of the string.
802    ///
803    /// # Examples
804    ///
805    /// ```
806    /// use cheetah_string::CheetahString;
807    ///
808    /// let s = CheetahString::from("line1\nline2\nline3");
809    /// let lines: Vec<&str> = s.lines().collect();
810    /// assert_eq!(lines, vec!["line1", "line2", "line3"]);
811    /// ```
812    #[inline]
813    pub fn lines(&self) -> impl Iterator<Item = &str> {
814        self.as_str().lines()
815    }
816
817    /// Returns an iterator over the characters of the string.
818    ///
819    /// # Examples
820    ///
821    /// ```
822    /// use cheetah_string::CheetahString;
823    ///
824    /// let s = CheetahString::from("hello");
825    /// let chars: Vec<char> = s.chars().collect();
826    /// assert_eq!(chars, vec!['h', 'e', 'l', 'l', 'o']);
827    /// let reversed: Vec<char> = s.chars().rev().collect();
828    /// assert_eq!(reversed, vec!['o', 'l', 'l', 'e', 'h']);
829    /// ```
830    #[inline]
831    pub fn chars(&self) -> str::Chars<'_> {
832        self.as_str().chars()
833    }
834
835    // Transformation methods - create new CheetahString
836
837    /// Returns a new `CheetahString` with all characters converted to uppercase.
838    ///
839    /// # Examples
840    ///
841    /// ```
842    /// use cheetah_string::CheetahString;
843    ///
844    /// let s = CheetahString::from("hello");
845    /// assert_eq!(s.to_uppercase(), "HELLO");
846    /// ```
847    #[inline]
848    pub fn to_uppercase(&self) -> CheetahString {
849        CheetahString::from_string(self.as_str().to_uppercase())
850    }
851
852    /// Returns a new `CheetahString` with all characters converted to lowercase.
853    ///
854    /// # Examples
855    ///
856    /// ```
857    /// use cheetah_string::CheetahString;
858    ///
859    /// let s = CheetahString::from("HELLO");
860    /// assert_eq!(s.to_lowercase(), "hello");
861    /// ```
862    #[inline]
863    pub fn to_lowercase(&self) -> CheetahString {
864        CheetahString::from_string(self.as_str().to_lowercase())
865    }
866
867    /// Replaces all occurrences of a pattern with another string.
868    ///
869    /// # Examples
870    ///
871    /// ```
872    /// use cheetah_string::CheetahString;
873    ///
874    /// let s = CheetahString::from("hello world");
875    /// assert_eq!(s.replace("world", "rust"), "hello rust");
876    /// ```
877    #[inline]
878    pub fn replace<P: AsRef<str>>(&self, from: P, to: &str) -> CheetahString {
879        CheetahString::from_string(self.as_str().replace(from.as_ref(), to))
880    }
881
882    /// Returns a new `CheetahString` with the specified range replaced.
883    ///
884    /// # Examples
885    ///
886    /// ```
887    /// use cheetah_string::CheetahString;
888    ///
889    /// let s = CheetahString::from("hello world");
890    /// assert_eq!(s.replacen("l", "L", 1), "heLlo world");
891    /// ```
892    #[inline]
893    pub fn replacen<P: AsRef<str>>(&self, from: P, to: &str, count: usize) -> CheetahString {
894        CheetahString::from_string(self.as_str().replacen(from.as_ref(), to, count))
895    }
896
897    /// Returns a substring as a new `CheetahString`.
898    ///
899    /// # Panics
900    ///
901    /// Panics if the indices are not on valid UTF-8 character boundaries.
902    ///
903    /// # Examples
904    ///
905    /// ```
906    /// use cheetah_string::CheetahString;
907    ///
908    /// let s = CheetahString::from("hello world");
909    /// assert_eq!(s.substring(0, 5), "hello");
910    /// assert_eq!(s.substring(6, 11), "world");
911    /// ```
912    #[inline]
913    pub fn substring(&self, start: usize, end: usize) -> CheetahString {
914        CheetahString::from_slice(&self.as_str()[start..end])
915    }
916
917    /// Repeats the string `n` times.
918    ///
919    /// # Examples
920    ///
921    /// ```
922    /// use cheetah_string::CheetahString;
923    ///
924    /// let s = CheetahString::from("abc");
925    /// assert_eq!(s.repeat(3), "abcabcabc");
926    /// ```
927    #[inline]
928    pub fn repeat(&self, n: usize) -> CheetahString {
929        CheetahString::from_string(self.as_str().repeat(n))
930    }
931
932    // Incremental building methods
933
934    /// Creates a new `CheetahString` with the specified capacity.
935    ///
936    /// The string will be able to hold at least `capacity` bytes without reallocating.
937    /// If `capacity` is less than or equal to the inline capacity (23 bytes),
938    /// an empty inline string is returned.
939    ///
940    /// # Examples
941    ///
942    /// ```
943    /// use cheetah_string::CheetahString;
944    ///
945    /// let mut s = CheetahString::with_capacity(100);
946    /// s.push_str("hello");
947    /// assert_eq!(s, "hello");
948    /// ```
949    #[inline]
950    pub fn with_capacity(capacity: usize) -> Self {
951        if capacity <= INLINE_CAPACITY {
952            CheetahString::empty()
953        } else {
954            CheetahString::from_builder_string(String::with_capacity(capacity))
955        }
956    }
957
958    #[inline]
959    fn push_str_internal(&mut self, string: &str) {
960        if string.is_empty() {
961            return;
962        }
963
964        match &mut self.inner {
965            InnerString::Inline { len, data } => {
966                let total_len = *len as usize + string.len();
967                if total_len <= INLINE_CAPACITY {
968                    data[*len as usize..total_len].copy_from_slice(string.as_bytes());
969                    *len = total_len as u8;
970                    return;
971                }
972            }
973            InnerString::Owned(s) => {
974                s.push_str(string);
975                return;
976            }
977            _ => {}
978        }
979
980        let total_len = self.len() + string.len();
981        let mut result = String::with_capacity(total_len);
982        result.push_str(self.as_str());
983        result.push_str(string);
984        *self = CheetahString::from_builder_string(result);
985    }
986
987    /// Appends a string slice to the end of this `CheetahString`.
988    ///
989    /// This method is optimized for incremental building and will:
990    /// - Mutate inline storage when possible
991    /// - Mutate owned heap storage in-place when capacity allows
992    /// - Only allocate when necessary
993    ///
994    /// # Examples
995    ///
996    /// ```
997    /// use cheetah_string::CheetahString;
998    ///
999    /// let mut s = CheetahString::from("Hello");
1000    /// s.push_str(" ");
1001    /// s.push_str("World");
1002    /// assert_eq!(s, "Hello World");
1003    /// ```
1004    #[inline]
1005    pub fn push_str(&mut self, string: &str) {
1006        self.push_str_internal(string);
1007    }
1008
1009    /// Reserves capacity for at least `additional` more bytes.
1010    ///
1011    /// This method will modify the internal representation if needed to ensure
1012    /// that the string can hold at least `additional` more bytes without reallocating.
1013    ///
1014    /// # Examples
1015    ///
1016    /// ```
1017    /// use cheetah_string::CheetahString;
1018    ///
1019    /// let mut s = CheetahString::from("hello");
1020    /// s.reserve(100);
1021    /// s.push_str(" world");
1022    /// ```
1023    #[inline]
1024    pub fn reserve(&mut self, additional: usize) {
1025        if additional == 0 {
1026            return;
1027        }
1028
1029        match &mut self.inner {
1030            InnerString::Inline { len, .. } if *len as usize + additional <= INLINE_CAPACITY => {
1031                return;
1032            }
1033            InnerString::Inline { .. } => {}
1034            InnerString::Owned(s) => {
1035                s.reserve(additional);
1036                return;
1037            }
1038            _ => {}
1039        }
1040
1041        let new_len = self.len() + additional;
1042        let mut s = String::with_capacity(new_len);
1043        s.push_str(self.as_str());
1044        *self = CheetahString::from_builder_string(s);
1045    }
1046}
1047
1048impl PartialEq for CheetahString {
1049    #[inline]
1050    fn eq(&self, other: &Self) -> bool {
1051        #[cfg(all(feature = "simd", target_arch = "x86_64"))]
1052        {
1053            crate::simd::eq_bytes(self.as_bytes(), other.as_bytes())
1054        }
1055        #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
1056        {
1057            self.as_str() == other.as_str()
1058        }
1059    }
1060}
1061
1062impl PartialEq<str> for CheetahString {
1063    #[inline]
1064    fn eq(&self, other: &str) -> bool {
1065        #[cfg(all(feature = "simd", target_arch = "x86_64"))]
1066        {
1067            crate::simd::eq_bytes(self.as_bytes(), other.as_bytes())
1068        }
1069        #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
1070        {
1071            self.as_str() == other
1072        }
1073    }
1074}
1075
1076impl PartialEq<String> for CheetahString {
1077    #[inline]
1078    fn eq(&self, other: &String) -> bool {
1079        #[cfg(all(feature = "simd", target_arch = "x86_64"))]
1080        {
1081            crate::simd::eq_bytes(self.as_bytes(), other.as_bytes())
1082        }
1083        #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
1084        {
1085            self.as_str() == other.as_str()
1086        }
1087    }
1088}
1089
1090impl PartialEq<Vec<u8>> for CheetahString {
1091    #[inline]
1092    fn eq(&self, other: &Vec<u8>) -> bool {
1093        self.as_bytes() == other.as_slice()
1094    }
1095}
1096
1097impl<'a> PartialEq<&'a str> for CheetahString {
1098    #[inline]
1099    fn eq(&self, other: &&'a str) -> bool {
1100        self.as_str() == *other
1101    }
1102}
1103
1104impl PartialEq<CheetahString> for str {
1105    #[inline]
1106    fn eq(&self, other: &CheetahString) -> bool {
1107        self == other.as_str()
1108    }
1109}
1110
1111impl PartialEq<CheetahString> for String {
1112    #[inline]
1113    fn eq(&self, other: &CheetahString) -> bool {
1114        self.as_str() == other.as_str()
1115    }
1116}
1117
1118impl PartialEq<CheetahString> for &str {
1119    #[inline]
1120    fn eq(&self, other: &CheetahString) -> bool {
1121        *self == other.as_str()
1122    }
1123}
1124
1125impl Eq for CheetahString {}
1126
1127impl PartialOrd for CheetahString {
1128    #[inline]
1129    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1130        Some(self.cmp(other))
1131    }
1132}
1133
1134impl Ord for CheetahString {
1135    #[inline]
1136    fn cmp(&self, other: &Self) -> Ordering {
1137        self.as_str().cmp(other.as_str())
1138    }
1139}
1140
1141impl Hash for CheetahString {
1142    #[inline]
1143    fn hash<H: Hasher>(&self, state: &mut H) {
1144        self.as_str().hash(state);
1145    }
1146}
1147
1148impl Display for CheetahString {
1149    #[inline]
1150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1151        self.as_str().fmt(f)
1152    }
1153}
1154
1155impl fmt::Debug for CheetahString {
1156    #[inline]
1157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1158        fmt::Debug::fmt(self.as_str(), f)
1159    }
1160}
1161
1162impl Borrow<str> for CheetahString {
1163    #[inline]
1164    fn borrow(&self) -> &str {
1165        self.as_str()
1166    }
1167}
1168
1169// Add trait implementations for string concatenation
1170
1171impl Add<&str> for CheetahString {
1172    type Output = CheetahString;
1173
1174    /// Concatenates a `CheetahString` with a string slice.
1175    ///
1176    /// # Examples
1177    ///
1178    /// ```
1179    /// use cheetah_string::CheetahString;
1180    ///
1181    /// let s = CheetahString::from("Hello");
1182    /// let result = s + " World";
1183    /// assert_eq!(result, "Hello World");
1184    /// ```
1185    #[inline]
1186    fn add(mut self, rhs: &str) -> Self::Output {
1187        self.push_str_internal(rhs);
1188        self
1189    }
1190}
1191
1192impl Add<&CheetahString> for CheetahString {
1193    type Output = CheetahString;
1194
1195    /// Concatenates two `CheetahString` values.
1196    ///
1197    /// # Examples
1198    ///
1199    /// ```
1200    /// use cheetah_string::CheetahString;
1201    ///
1202    /// let s1 = CheetahString::from("Hello");
1203    /// let s2 = CheetahString::from(" World");
1204    /// let result = s1 + &s2;
1205    /// assert_eq!(result, "Hello World");
1206    /// ```
1207    #[inline]
1208    fn add(mut self, rhs: &CheetahString) -> Self::Output {
1209        self.push_str_internal(rhs.as_str());
1210        self
1211    }
1212}
1213
1214impl Add<String> for CheetahString {
1215    type Output = CheetahString;
1216
1217    /// Concatenates a `CheetahString` with a `String`.
1218    ///
1219    /// # Examples
1220    ///
1221    /// ```
1222    /// use cheetah_string::CheetahString;
1223    ///
1224    /// let s = CheetahString::from("Hello");
1225    /// let result = s + String::from(" World");
1226    /// assert_eq!(result, "Hello World");
1227    /// ```
1228    #[inline]
1229    fn add(mut self, rhs: String) -> Self::Output {
1230        if self.is_empty() {
1231            return CheetahString::from_string_owned(rhs);
1232        }
1233
1234        self.push_str_internal(&rhs);
1235        self
1236    }
1237}
1238
1239impl AddAssign<&str> for CheetahString {
1240    /// Appends a string slice to a `CheetahString`.
1241    ///
1242    /// # Examples
1243    ///
1244    /// ```
1245    /// use cheetah_string::CheetahString;
1246    ///
1247    /// let mut s = CheetahString::from("Hello");
1248    /// s += " World";
1249    /// assert_eq!(s, "Hello World");
1250    /// ```
1251    #[inline]
1252    fn add_assign(&mut self, rhs: &str) {
1253        self.push_str_internal(rhs);
1254    }
1255}
1256
1257impl AddAssign<&CheetahString> for CheetahString {
1258    /// Appends a `CheetahString` to another `CheetahString`.
1259    ///
1260    /// # Examples
1261    ///
1262    /// ```
1263    /// use cheetah_string::CheetahString;
1264    ///
1265    /// let mut s1 = CheetahString::from("Hello");
1266    /// let s2 = CheetahString::from(" World");
1267    /// s1 += &s2;
1268    /// assert_eq!(s1, "Hello World");
1269    /// ```
1270    #[inline]
1271    fn add_assign(&mut self, rhs: &CheetahString) {
1272        self.push_str_internal(rhs.as_str());
1273    }
1274}
1275
1276/// Maximum capacity for inline string storage (23 bytes + 1 byte for length = 24 bytes total)
1277const INLINE_CAPACITY: usize = 23;
1278
1279/// The `InnerString` enum represents different types of string storage.
1280///
1281/// This enum uses Small String Optimization (SSO) to avoid heap allocations for short strings.
1282///
1283/// Variants:
1284///
1285/// * `Inline` - Inline storage for strings <= 23 bytes (zero heap allocations).
1286/// * `Static(&'static str)` - A static string slice (zero heap allocations).
1287/// * `Shared(Arc<str>)` - A reference-counted string slice (single heap allocation, optimized).
1288/// * `Owned(String)` - An owned heap string used for builder-style mutation.
1289#[derive(Clone)]
1290pub(super) enum InnerString {
1291    /// Inline storage for short strings (up to 23 bytes).
1292    /// Stores the length and data directly without heap allocation.
1293    Inline {
1294        len: u8,
1295        data: [u8; INLINE_CAPACITY],
1296    },
1297    /// Static string slice with 'static lifetime.
1298    Static(&'static str),
1299    /// Reference-counted string slice (single heap allocation).
1300    /// Preferred for long immutable strings created from owned or borrowed data.
1301    Shared(Arc<str>),
1302    /// Owned heap-allocated string used when exclusive mutability matters.
1303    Owned(String),
1304}
1305
1306// Sealed trait pattern to support both &str and char in starts_with/ends_with/contains
1307mod private {
1308    use alloc::string::String;
1309
1310    pub trait Sealed {}
1311    impl Sealed for char {}
1312    impl Sealed for &str {}
1313    impl Sealed for &String {}
1314
1315    pub trait SplitSealed {}
1316    impl SplitSealed for char {}
1317    impl SplitSealed for &str {}
1318}
1319
1320/// A pattern that can be used with `starts_with` and `ends_with` methods.
1321pub trait StrPattern: private::Sealed {
1322    #[doc(hidden)]
1323    fn as_str_pattern(&self) -> StrPatternImpl<'_>;
1324}
1325
1326#[doc(hidden)]
1327pub enum StrPatternImpl<'a> {
1328    Char(char),
1329    Str(&'a str),
1330}
1331
1332impl StrPattern for char {
1333    #[inline]
1334    fn as_str_pattern(&self) -> StrPatternImpl<'_> {
1335        StrPatternImpl::Char(*self)
1336    }
1337}
1338
1339impl StrPattern for &str {
1340    #[inline]
1341    fn as_str_pattern(&self) -> StrPatternImpl<'_> {
1342        StrPatternImpl::Str(self)
1343    }
1344}
1345
1346impl StrPattern for &String {
1347    #[inline]
1348    fn as_str_pattern(&self) -> StrPatternImpl<'_> {
1349        StrPatternImpl::Str(self.as_str())
1350    }
1351}
1352
1353/// A pattern that can be used with `split` method.
1354pub trait SplitPattern<'a>: private::SplitSealed {
1355    #[doc(hidden)]
1356    fn split_str(self, s: &'a str) -> SplitWrapper<'a>;
1357}
1358
1359impl SplitPattern<'_> for char {
1360    fn split_str(self, s: &str) -> SplitWrapper<'_> {
1361        SplitWrapper::Char(s.split(self))
1362    }
1363}
1364
1365impl<'a> SplitPattern<'a> for &'a str {
1366    fn split_str(self, s: &'a str) -> SplitWrapper<'a> {
1367        let inner = match single_char_pattern(self) {
1368            Some(ch) => SplitStrInner::Char(s.split(ch)),
1369            None => SplitStrInner::Str(s.split(self)),
1370        };
1371
1372        SplitWrapper::Str(SplitStr(inner))
1373    }
1374}
1375
1376/// Helper struct for splitting strings by a string pattern
1377pub struct SplitStr<'a>(SplitStrInner<'a>);
1378
1379enum SplitStrInner<'a> {
1380    Str(str::Split<'a, &'a str>),
1381    Char(str::Split<'a, char>),
1382}
1383
1384#[inline]
1385fn single_char_pattern(pattern: &str) -> Option<char> {
1386    let mut chars = pattern.chars();
1387    let ch = chars.next()?;
1388
1389    if chars.next().is_none() {
1390        Some(ch)
1391    } else {
1392        None
1393    }
1394}
1395
1396impl<'a> Iterator for SplitStr<'a> {
1397    type Item = &'a str;
1398
1399    fn next(&mut self) -> Option<Self::Item> {
1400        match &mut self.0 {
1401            SplitStrInner::Str(iter) => iter.next(),
1402            SplitStrInner::Char(iter) => iter.next(),
1403        }
1404    }
1405}
1406
1407/// Wrapper for split iterator that supports both char and str patterns
1408pub enum SplitWrapper<'a> {
1409    #[doc(hidden)]
1410    Char(str::Split<'a, char>),
1411    #[doc(hidden)]
1412    Str(SplitStr<'a>),
1413}
1414
1415impl<'a> Iterator for SplitWrapper<'a> {
1416    type Item = &'a str;
1417
1418    fn next(&mut self) -> Option<Self::Item> {
1419        match self {
1420            SplitWrapper::Char(iter) => iter.next(),
1421            SplitWrapper::Str(iter) => iter.next(),
1422        }
1423    }
1424}
1425
1426impl<'a> DoubleEndedIterator for SplitWrapper<'a> {
1427    fn next_back(&mut self) -> Option<Self::Item> {
1428        match self {
1429            SplitWrapper::Char(iter) => iter.next_back(),
1430            SplitWrapper::Str(_) => {
1431                // String pattern split doesn't support reverse iteration
1432                // This is consistent with std::str::Split<&str>
1433                panic!("split with string pattern does not support reverse iteration")
1434            }
1435        }
1436    }
1437}
1438
1439#[cfg(test)]
1440mod tests {
1441    use super::*;
1442    use alloc::{format, vec};
1443
1444    #[test]
1445    fn with_capacity_above_inline_uses_heap_storage() {
1446        let s = CheetahString::with_capacity(INLINE_CAPACITY + 8);
1447
1448        match &s.inner {
1449            InnerString::Owned(inner) => {
1450                assert!(inner.capacity() >= INLINE_CAPACITY + 8);
1451            }
1452            other => panic!(
1453                "expected heap-backed storage from with_capacity, got {:?}",
1454                core::mem::discriminant(other)
1455            ),
1456        }
1457    }
1458
1459    #[test]
1460    fn push_str_promotes_builder_growth_to_owned_storage() {
1461        let suffix = "a".repeat(INLINE_CAPACITY);
1462        let expected = format!("hello{suffix}");
1463        let mut s = CheetahString::from("hello");
1464
1465        s.push_str(&suffix);
1466
1467        match &s.inner {
1468            InnerString::Owned(inner) => {
1469                assert_eq!(inner.as_str(), expected.as_str());
1470                assert!(inner.capacity() >= expected.len());
1471            }
1472            other => panic!(
1473                "expected owned heap storage after builder growth, got {:?}",
1474                core::mem::discriminant(other)
1475            ),
1476        }
1477    }
1478
1479    #[test]
1480    fn long_borrowed_str_uses_shared_storage() {
1481        let value = "a".repeat(INLINE_CAPACITY + 1);
1482        let s = CheetahString::from_slice(&value);
1483
1484        match &s.inner {
1485            InnerString::Shared(inner) => assert_eq!(inner.as_ref(), value.as_str()),
1486            other => panic!(
1487                "expected Shared for long borrowed input, got {:?}",
1488                core::mem::discriminant(other)
1489            ),
1490        }
1491    }
1492
1493    #[test]
1494    fn try_from_vec_short_input_uses_inline_storage() {
1495        let s = CheetahString::try_from_vec(b"hello".to_vec()).expect("valid utf-8");
1496
1497        match &s.inner {
1498            InnerString::Inline { len, data } => {
1499                assert_eq!(*len as usize, 5);
1500                assert_eq!(&data[..5], b"hello");
1501            }
1502            other => panic!(
1503                "expected inline storage for short validated Vec<u8>, got {:?}",
1504                core::mem::discriminant(other)
1505            ),
1506        }
1507    }
1508
1509    #[test]
1510    fn long_vec_conversion_uses_owned_storage() {
1511        let value = "a".repeat(INLINE_CAPACITY + 1).into_bytes();
1512        let s = CheetahString::try_from_vec(value).expect("valid utf-8");
1513
1514        match &s.inner {
1515            InnerString::Owned(inner) => {
1516                assert_eq!(inner.len(), INLINE_CAPACITY + 1);
1517                assert_eq!(inner.as_bytes(), vec![b'a'; INLINE_CAPACITY + 1].as_slice());
1518            }
1519            other => panic!(
1520                "expected Owned for long Vec<u8> conversion, got {:?}",
1521                core::mem::discriminant(other)
1522            ),
1523        }
1524    }
1525}