java_string/
slice.rs

1use std::borrow::Cow;
2use std::collections::Bound;
3use std::fmt::{Debug, Display, Formatter, Write};
4use std::hash::{Hash, Hasher};
5use std::ops::{
6    Add, AddAssign, Index, IndexMut, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive,
7    RangeTo, RangeToInclusive,
8};
9use std::rc::Rc;
10use std::str::FromStr;
11use std::sync::Arc;
12use std::{ptr, slice};
13
14use crate::char::EscapeDebugExtArgs;
15use crate::validations::{
16    run_utf8_full_validation_from_semi, run_utf8_semi_validation, slice_error_fail,
17    str_end_index_overflow_fail,
18};
19use crate::{
20    Bytes, CharEscapeIter, CharIndices, Chars, EscapeDebug, EscapeDefault, EscapeUnicode,
21    JavaCodePoint, JavaStrPattern, JavaString, Lines, MatchIndices, Matches, ParseError,
22    RMatchIndices, RMatches, RSplit, RSplitN, RSplitTerminator, Split, SplitAsciiWhitespace,
23    SplitInclusive, SplitN, SplitTerminator, SplitWhitespace, Utf8Error,
24};
25
26#[derive(PartialEq, Eq, PartialOrd, Ord)]
27#[repr(transparent)]
28pub struct JavaStr {
29    inner: [u8],
30}
31
32#[allow(clippy::multiple_inherent_impl)]
33impl JavaStr {
34    /// Converts `v` to a `&JavaStr` if it is fully-valid UTF-8, i.e. UTF-8
35    /// without surrogate code points. See [`std::str::from_utf8`].
36    #[inline]
37    pub const fn from_full_utf8(v: &[u8]) -> Result<&JavaStr, Utf8Error> {
38        match std::str::from_utf8(v) {
39            Ok(str) => Ok(JavaStr::from_str(str)),
40            Err(err) => Err(Utf8Error::from_std(err)),
41        }
42    }
43
44    /// Converts `v` to a `&mut JavaStr` if it is fully-valid UTF-8, i.e. UTF-8
45    /// without surrogate code points. See [`std::str::from_utf8_mut`].
46    #[inline]
47    pub fn from_full_utf8_mut(v: &mut [u8]) -> Result<&mut JavaStr, Utf8Error> {
48        match std::str::from_utf8_mut(v) {
49            Ok(str) => Ok(JavaStr::from_mut_str(str)),
50            Err(err) => Err(Utf8Error::from_std(err)),
51        }
52    }
53
54    /// Converts `v` to a `&JavaStr` if it is semi-valid UTF-8, i.e. UTF-8
55    /// with surrogate code points.
56    pub fn from_semi_utf8(v: &[u8]) -> Result<&JavaStr, Utf8Error> {
57        match run_utf8_semi_validation(v) {
58            Ok(()) => Ok(unsafe { JavaStr::from_semi_utf8_unchecked(v) }),
59            Err(err) => Err(err),
60        }
61    }
62
63    /// Converts `v` to a `&mut JavaStr` if it is semi-valid UTF-8, i.e. UTF-8
64    /// with surrogate code points.
65    pub fn from_semi_utf8_mut(v: &mut [u8]) -> Result<&mut JavaStr, Utf8Error> {
66        match run_utf8_semi_validation(v) {
67            Ok(()) => Ok(unsafe { JavaStr::from_semi_utf8_unchecked_mut(v) }),
68            Err(err) => Err(err),
69        }
70    }
71
72    /// # Safety
73    ///
74    /// The parameter must be in semi-valid UTF-8 format, that is, UTF-8 plus
75    /// surrogate code points.
76    #[inline]
77    #[must_use]
78    pub const unsafe fn from_semi_utf8_unchecked(v: &[u8]) -> &JavaStr {
79        // SAFETY: the caller must guarantee that the bytes `v` are valid UTF-8, minus
80        // the absence of surrogate chars. Also relies on `&JavaStr` and `&[u8]`
81        // having the same layout.
82        std::mem::transmute(v)
83    }
84
85    /// # Safety
86    ///
87    /// The parameter must be in semi-valid UTF-8 format, that is, UTF-8 plus
88    /// surrogate code points.
89    #[inline]
90    #[must_use]
91    pub unsafe fn from_semi_utf8_unchecked_mut(v: &mut [u8]) -> &mut JavaStr {
92        // SAFETY: see from_semi_utf8_unchecked
93        std::mem::transmute(v)
94    }
95
96    #[inline]
97    #[must_use]
98    pub const fn from_str(str: &str) -> &JavaStr {
99        unsafe {
100            // SAFETY: the input str is guaranteed to have valid UTF-8.
101            JavaStr::from_semi_utf8_unchecked(str.as_bytes())
102        }
103    }
104
105    #[inline]
106    #[must_use]
107    pub fn from_mut_str(str: &mut str) -> &mut JavaStr {
108        unsafe {
109            // SAFETY: the input str is guaranteed to have valid UTF-8.
110            JavaStr::from_semi_utf8_unchecked_mut(str.as_bytes_mut())
111        }
112    }
113
114    #[inline]
115    #[must_use]
116    pub fn from_boxed_str(v: Box<str>) -> Box<JavaStr> {
117        unsafe { JavaStr::from_boxed_semi_utf8_unchecked(v.into_boxed_bytes()) }
118    }
119
120    /// # Safety
121    ///
122    /// The parameter must be in semi-valid UTF-8 format, that is, UTF-8 plus
123    /// surrogate code points.
124    #[inline]
125    #[must_use]
126    pub unsafe fn from_boxed_semi_utf8_unchecked(v: Box<[u8]>) -> Box<JavaStr> {
127        unsafe { Box::from_raw(Box::into_raw(v) as *mut JavaStr) }
128    }
129
130    /// See [`str::as_bytes`].
131    #[inline]
132    #[must_use]
133    pub const fn as_bytes(&self) -> &[u8] {
134        &self.inner
135    }
136
137    /// See [`str::as_bytes_mut`].
138    ///
139    /// # Safety
140    ///
141    /// The returned slice must not have invalid UTF-8 written to it, besides
142    /// surrogate pairs.
143    #[inline]
144    #[must_use]
145    pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
146        &mut self.inner
147    }
148
149    /// See [`str::as_mut_ptr`].
150    #[inline]
151    #[must_use]
152    pub fn as_mut_ptr(&mut self) -> *mut u8 {
153        self.inner.as_mut_ptr()
154    }
155
156    /// See [`str::as_ptr`].
157    #[inline]
158    #[must_use]
159    pub const fn as_ptr(&self) -> *const u8 {
160        self.inner.as_ptr()
161    }
162
163    /// Tries to convert this `&JavaStr` to a `&str`, returning an error if
164    /// it is not fully valid UTF-8, i.e. has no surrogate code points.
165    pub const fn as_str(&self) -> Result<&str, Utf8Error> {
166        // Manual implementation of Option::map since it's not const
167        match run_utf8_full_validation_from_semi(self.as_bytes()) {
168            Ok(..) => unsafe {
169                // SAFETY: we were already semi-valid, and full validation just succeeded.
170                Ok(self.as_str_unchecked())
171            },
172            Err(err) => Err(err),
173        }
174    }
175
176    /// # Safety
177    ///
178    /// This string must be fully valid UTF-8, i.e. have no surrogate code
179    /// points.
180    #[inline]
181    #[must_use]
182    pub const unsafe fn as_str_unchecked(&self) -> &str {
183        std::str::from_utf8_unchecked(self.as_bytes())
184    }
185
186    /// Converts this `&JavaStr` to a `Cow<str>`, replacing surrogate code
187    /// points with the replacement character �.
188    ///
189    /// ```
190    /// # use std::borrow::Cow;
191    /// # use java_string::{JavaCodePoint, JavaStr, JavaString};
192    /// let s = JavaStr::from_str("Hello 🦀 World!");
193    /// let result = s.as_str_lossy();
194    /// assert!(matches!(result, Cow::Borrowed(_)));
195    /// assert_eq!(result, "Hello 🦀 World!");
196    ///
197    /// let s = JavaString::from("Hello ")
198    ///     + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
199    ///     + JavaStr::from_str(" World!");
200    /// let result = s.as_str_lossy();
201    /// assert!(matches!(result, Cow::Owned(_)));
202    /// assert_eq!(result, "Hello � World!");
203    /// ```
204    #[must_use]
205    pub fn as_str_lossy(&self) -> Cow<'_, str> {
206        match run_utf8_full_validation_from_semi(self.as_bytes()) {
207            Ok(()) => unsafe {
208                // SAFETY: validation succeeded
209                Cow::Borrowed(self.as_str_unchecked())
210            },
211            Err(error) => unsafe {
212                // SAFETY: invalid parts of string are converted to replacement char
213                Cow::Owned(
214                    self.transform_invalid_string(error, str::to_owned, |_| {
215                        JavaStr::from_str("\u{FFFD}")
216                    })
217                    .into_string_unchecked(),
218                )
219            },
220        }
221    }
222
223    /// See [`str::bytes`].
224    #[inline]
225    pub fn bytes(&self) -> Bytes<'_> {
226        Bytes {
227            inner: self.inner.iter().copied(),
228        }
229    }
230
231    /// See [`str::char_indices`].
232    #[inline]
233    pub fn char_indices(&self) -> CharIndices<'_> {
234        CharIndices {
235            front_offset: 0,
236            inner: self.chars(),
237        }
238    }
239
240    /// See [`str::chars`].
241    #[inline]
242    pub fn chars(&self) -> Chars<'_> {
243        Chars {
244            inner: self.inner.iter(),
245        }
246    }
247
248    /// See [`str::contains`].
249    ///
250    /// ```
251    /// # use java_string::JavaStr;
252    /// let bananas = JavaStr::from_str("bananas");
253    ///
254    /// assert!(bananas.contains("nana"));
255    /// assert!(!bananas.contains("apples"));
256    /// ```
257    #[inline]
258    #[must_use]
259    pub fn contains<P>(&self, mut pat: P) -> bool
260    where
261        P: JavaStrPattern,
262    {
263        pat.find_in(self).is_some()
264    }
265
266    /// See [`str::ends_with`].
267    ///
268    /// ```
269    /// # use java_string::JavaStr;
270    /// let bananas = JavaStr::from_str("bananas");
271    ///
272    /// assert!(bananas.ends_with("anas"));
273    /// assert!(!bananas.ends_with("nana"));
274    /// ```
275    #[inline]
276    #[must_use]
277    pub fn ends_with<P>(&self, mut pat: P) -> bool
278    where
279        P: JavaStrPattern,
280    {
281        pat.suffix_len_in(self).is_some()
282    }
283
284    /// See [`str::eq_ignore_ascii_case`].
285    #[inline]
286    #[must_use]
287    pub fn eq_ignore_ascii_case(&self, other: &str) -> bool {
288        self.as_bytes().eq_ignore_ascii_case(other.as_bytes())
289    }
290
291    /// See [`str::eq_ignore_ascii_case`].
292    #[inline]
293    #[must_use]
294    pub fn eq_java_ignore_ascii_case(&self, other: &JavaStr) -> bool {
295        self.as_bytes().eq_ignore_ascii_case(other.as_bytes())
296    }
297
298    /// See [`str::escape_debug`].
299    ///
300    /// ```
301    /// # use java_string::JavaStr;
302    /// assert_eq!(
303    ///     JavaStr::from_str("❤\n!").escape_debug().to_string(),
304    ///     "❤\\n!"
305    /// );
306    /// ```
307    #[inline]
308    pub fn escape_debug(&self) -> EscapeDebug<'_> {
309        #[inline]
310        fn escape_first(first: JavaCodePoint) -> CharEscapeIter {
311            first.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL)
312        }
313        #[inline]
314        fn escape_rest(char: JavaCodePoint) -> CharEscapeIter {
315            char.escape_debug_ext(EscapeDebugExtArgs {
316                escape_single_quote: true,
317                escape_double_quote: true,
318            })
319        }
320
321        let mut chars = self.chars();
322        EscapeDebug {
323            inner: chars
324                .next()
325                .map(escape_first as fn(JavaCodePoint) -> CharEscapeIter)
326                .into_iter()
327                .flatten()
328                .chain(chars.flat_map(escape_rest as fn(JavaCodePoint) -> CharEscapeIter)),
329        }
330    }
331
332    /// See [`str::escape_default`].
333    ///
334    /// ```
335    /// # use java_string::JavaStr;
336    /// assert_eq!(
337    ///     JavaStr::from_str("❤\n!").escape_default().to_string(),
338    ///     "\\u{2764}\\n!"
339    /// );
340    /// ```
341    #[inline]
342    pub fn escape_default(&self) -> EscapeDefault<'_> {
343        EscapeDefault {
344            inner: self.chars().flat_map(JavaCodePoint::escape_default),
345        }
346    }
347
348    /// See [`str::escape_unicode`].
349    ///
350    /// ```
351    /// # use java_string::JavaStr;
352    /// assert_eq!(
353    ///     JavaStr::from_str("❤\n!").escape_unicode().to_string(),
354    ///     "\\u{2764}\\u{a}\\u{21}"
355    /// );
356    /// ```
357    #[inline]
358    pub fn escape_unicode(&self) -> EscapeUnicode<'_> {
359        EscapeUnicode {
360            inner: self.chars().flat_map(JavaCodePoint::escape_unicode),
361        }
362    }
363
364    /// See [`str::find`].
365    ///
366    /// ```
367    /// let s = "Löwe 老虎 Léopard Gepardi";
368    ///
369    /// assert_eq!(s.find('L'), Some(0));
370    /// assert_eq!(s.find('é'), Some(14));
371    /// assert_eq!(s.find("par"), Some(17));
372    ///
373    /// let x: &[_] = &['1', '2'];
374    /// assert_eq!(s.find(x), None);
375    /// ```
376    #[inline]
377    #[must_use]
378    pub fn find<P>(&self, mut pat: P) -> Option<usize>
379    where
380        P: JavaStrPattern,
381    {
382        pat.find_in(self).map(|(index, _)| index)
383    }
384
385    /// See [`str::get`].
386    ///
387    /// ```
388    /// # use java_string::{JavaStr, JavaString};
389    /// let v = JavaString::from("🗻∈🌏");
390    ///
391    /// assert_eq!(Some(JavaStr::from_str("🗻")), v.get(0..4));
392    ///
393    /// // indices not on UTF-8 sequence boundaries
394    /// assert!(v.get(1..).is_none());
395    /// assert!(v.get(..8).is_none());
396    ///
397    /// // out of bounds
398    /// assert!(v.get(..42).is_none());
399    /// ```
400    #[inline]
401    #[must_use]
402    pub fn get<I>(&self, i: I) -> Option<&JavaStr>
403    where
404        I: JavaStrSliceIndex,
405    {
406        i.get(self)
407    }
408
409    /// See [`str::get_mut`].
410    #[inline]
411    #[must_use]
412    pub fn get_mut<I>(&mut self, i: I) -> Option<&mut JavaStr>
413    where
414        I: JavaStrSliceIndex,
415    {
416        i.get_mut(self)
417    }
418
419    /// See [`str::get_unchecked`].
420    ///
421    /// # Safety
422    ///
423    /// - The starting index must not exceed the ending index
424    /// - Indexes must be within bounds of the original slice
425    /// - Indexes must lie on UTF-8 sequence boundaries
426    #[inline]
427    #[must_use]
428    pub unsafe fn get_unchecked<I>(&self, i: I) -> &JavaStr
429    where
430        I: JavaStrSliceIndex,
431    {
432        unsafe { &*i.get_unchecked(self) }
433    }
434
435    /// See [`str::get_unchecked_mut`].
436    ///
437    /// # Safety
438    ///
439    /// - The starting index must not exceed the ending index
440    /// - Indexes must be within bounds of the original slice
441    /// - Indexes must lie on UTF-8 sequence boundaries
442    #[inline]
443    #[must_use]
444    pub unsafe fn get_unchecked_mut<I>(&mut self, i: I) -> &mut JavaStr
445    where
446        I: JavaStrSliceIndex,
447    {
448        unsafe { &mut *i.get_unchecked_mut(self) }
449    }
450
451    /// See [`str::into_boxed_bytes`].
452    #[inline]
453    #[must_use]
454    pub fn into_boxed_bytes(self: Box<JavaStr>) -> Box<[u8]> {
455        unsafe { Box::from_raw(Box::into_raw(self) as *mut [u8]) }
456    }
457
458    /// See [`str::into_string`].
459    #[inline]
460    #[must_use]
461    pub fn into_string(self: Box<JavaStr>) -> JavaString {
462        let slice = self.into_boxed_bytes();
463        unsafe { JavaString::from_semi_utf8_unchecked(slice.into_vec()) }
464    }
465
466    /// See [`str::is_ascii`].
467    #[inline]
468    #[must_use]
469    pub fn is_ascii(&self) -> bool {
470        self.as_bytes().is_ascii()
471    }
472
473    /// See [`str::is_char_boundary`].
474    #[inline]
475    #[must_use]
476    pub fn is_char_boundary(&self, index: usize) -> bool {
477        // 0 is always ok.
478        // Test for 0 explicitly so that it can optimize out the check
479        // easily and skip reading string data for that case.
480        // Note that optimizing `self.get(..index)` relies on this.
481        if index == 0 {
482            return true;
483        }
484
485        match self.as_bytes().get(index) {
486            // For `None` we have two options:
487            //
488            // - index == self.len() Empty strings are valid, so return true
489            // - index > self.len() In this case return false
490            //
491            // The check is placed exactly here, because it improves generated
492            // code on higher opt-levels. See https://github.com/rust-lang/rust/pull/84751 for more details.
493            None => index == self.len(),
494
495            Some(&b) => {
496                // This is bit magic equivalent to: b < 128 || b >= 192
497                (b as i8) >= -0x40
498            }
499        }
500    }
501
502    pub(crate) fn floor_char_boundary(&self, index: usize) -> usize {
503        if index >= self.len() {
504            self.len()
505        } else {
506            let lower_bound = index.saturating_sub(3);
507            let new_index = self.as_bytes()[lower_bound..=index].iter().rposition(|b| {
508                // This is bit magic equivalent to: b < 128 || b >= 192
509                (*b as i8) >= -0x40
510            });
511
512            // SAFETY: we know that the character boundary will be within four bytes
513            unsafe { lower_bound + new_index.unwrap_unchecked() }
514        }
515    }
516
517    /// See [`str::is_empty`].
518    #[inline]
519    #[must_use]
520    pub fn is_empty(&self) -> bool {
521        self.len() == 0
522    }
523
524    /// See [`str::len`].
525    #[inline]
526    #[must_use]
527    pub fn len(&self) -> usize {
528        self.inner.len()
529    }
530
531    /// See [`str::lines`].
532    #[inline]
533    pub fn lines(&self) -> Lines<'_> {
534        Lines {
535            inner: self.split_inclusive('\n').map(|line| {
536                let Some(line) = line.strip_suffix('\n') else {
537                    return line;
538                };
539                let Some(line) = line.strip_suffix('\r') else {
540                    return line;
541                };
542                line
543            }),
544        }
545    }
546
547    /// See [`str::make_ascii_lowercase`].
548    #[inline]
549    pub fn make_ascii_lowercase(&mut self) {
550        // SAFETY: changing ASCII letters only does not invalidate UTF-8.
551        let me = unsafe { self.as_bytes_mut() };
552        me.make_ascii_lowercase()
553    }
554
555    /// See [`str::make_ascii_uppercase`].
556    #[inline]
557    pub fn make_ascii_uppercase(&mut self) {
558        // SAFETY: changing ASCII letters only does not invalidate UTF-8.
559        let me = unsafe { self.as_bytes_mut() };
560        me.make_ascii_uppercase()
561    }
562
563    /// See [`str::match_indices`].
564    ///
565    /// ```
566    /// # use java_string::JavaStr;
567    /// let v: Vec<_> = JavaStr::from_str("abcXXXabcYYYabc")
568    ///     .match_indices("abc")
569    ///     .collect();
570    /// assert_eq!(
571    ///     v,
572    ///     [
573    ///         (0, JavaStr::from_str("abc")),
574    ///         (6, JavaStr::from_str("abc")),
575    ///         (12, JavaStr::from_str("abc"))
576    ///     ]
577    /// );
578    ///
579    /// let v: Vec<_> = JavaStr::from_str("1abcabc2").match_indices("abc").collect();
580    /// assert_eq!(
581    ///     v,
582    ///     [(1, JavaStr::from_str("abc")), (4, JavaStr::from_str("abc"))]
583    /// );
584    ///
585    /// let v: Vec<_> = JavaStr::from_str("ababa").match_indices("aba").collect();
586    /// assert_eq!(v, [(0, JavaStr::from_str("aba"))]); // only the first `aba`
587    /// ```
588    #[inline]
589    pub fn match_indices<P>(&self, pat: P) -> MatchIndices<P>
590    where
591        P: JavaStrPattern,
592    {
593        MatchIndices {
594            str: self,
595            start: 0,
596            pat,
597        }
598    }
599
600    /// See [`str::matches`].
601    ///
602    /// ```
603    /// # use java_string::{JavaCodePoint, JavaStr};
604    /// let v: Vec<&JavaStr> = JavaStr::from_str("abcXXXabcYYYabc")
605    ///     .matches("abc")
606    ///     .collect();
607    /// assert_eq!(
608    ///     v,
609    ///     [
610    ///         JavaStr::from_str("abc"),
611    ///         JavaStr::from_str("abc"),
612    ///         JavaStr::from_str("abc")
613    ///     ]
614    /// );
615    ///
616    /// let v: Vec<&JavaStr> = JavaStr::from_str("1abc2abc3")
617    ///     .matches(JavaCodePoint::is_numeric)
618    ///     .collect();
619    /// assert_eq!(
620    ///     v,
621    ///     [
622    ///         JavaStr::from_str("1"),
623    ///         JavaStr::from_str("2"),
624    ///         JavaStr::from_str("3")
625    ///     ]
626    /// );
627    /// ```
628    #[inline]
629    pub fn matches<P>(&self, pat: P) -> Matches<P>
630    where
631        P: JavaStrPattern,
632    {
633        Matches { str: self, pat }
634    }
635
636    /// See [`str::parse`].
637    #[inline]
638    pub fn parse<F>(&self) -> Result<F, ParseError<<F as FromStr>::Err>>
639    where
640        F: FromStr,
641    {
642        match self.as_str() {
643            Ok(str) => str.parse().map_err(ParseError::Err),
644            Err(err) => Err(ParseError::InvalidUtf8(err)),
645        }
646    }
647
648    /// See [`str::repeat`].
649    #[inline]
650    #[must_use]
651    pub fn repeat(&self, n: usize) -> JavaString {
652        unsafe { JavaString::from_semi_utf8_unchecked(self.as_bytes().repeat(n)) }
653    }
654
655    /// See [`str::replace`].
656    ///
657    /// ```
658    /// # use java_string::JavaStr;
659    /// let s = JavaStr::from_str("this is old");
660    ///
661    /// assert_eq!("this is new", s.replace("old", "new"));
662    /// assert_eq!("than an old", s.replace("is", "an"));
663    /// ```
664    #[inline]
665    #[must_use]
666    pub fn replace<P>(&self, from: P, to: &str) -> JavaString
667    where
668        P: JavaStrPattern,
669    {
670        self.replace_java(from, JavaStr::from_str(to))
671    }
672
673    /// See [`str::replace`].
674    #[inline]
675    #[must_use]
676    pub fn replace_java<P>(&self, from: P, to: &JavaStr) -> JavaString
677    where
678        P: JavaStrPattern,
679    {
680        let mut result = JavaString::new();
681        let mut last_end = 0;
682        for (start, part) in self.match_indices(from) {
683            result.push_java_str(unsafe { self.get_unchecked(last_end..start) });
684            result.push_java_str(to);
685            last_end = start + part.len();
686        }
687        result.push_java_str(unsafe { self.get_unchecked(last_end..self.len()) });
688        result
689    }
690
691    /// See [`str::replacen`].
692    ///
693    /// ```
694    /// # use java_string::{JavaCodePoint, JavaStr};
695    /// let s = JavaStr::from_str("foo foo 123 foo");
696    /// assert_eq!("new new 123 foo", s.replacen("foo", "new", 2));
697    /// assert_eq!("faa fao 123 foo", s.replacen('o', "a", 3));
698    /// assert_eq!(
699    ///     "foo foo new23 foo",
700    ///     s.replacen(JavaCodePoint::is_numeric, "new", 1)
701    /// );
702    /// ```
703    #[inline]
704    #[must_use]
705    pub fn replacen<P>(&self, from: P, to: &str, count: usize) -> JavaString
706    where
707        P: JavaStrPattern,
708    {
709        self.replacen_java(from, JavaStr::from_str(to), count)
710    }
711
712    /// See [`str::replacen`].
713    #[inline]
714    #[must_use]
715    pub fn replacen_java<P>(&self, from: P, to: &JavaStr, count: usize) -> JavaString
716    where
717        P: JavaStrPattern,
718    {
719        // Hope to reduce the times of re-allocation
720        let mut result = JavaString::with_capacity(32);
721        let mut last_end = 0;
722        for (start, part) in self.match_indices(from).take(count) {
723            result.push_java_str(unsafe { self.get_unchecked(last_end..start) });
724            result.push_java_str(to);
725            last_end = start + part.len();
726        }
727        result.push_java_str(unsafe { self.get_unchecked(last_end..self.len()) });
728        result
729    }
730
731    /// See [`str::rfind`].
732    ///
733    /// ```
734    /// # use java_string::JavaStr;
735    /// let s = JavaStr::from_str("Löwe 老虎 Léopard Gepardi");
736    ///
737    /// assert_eq!(s.rfind('L'), Some(13));
738    /// assert_eq!(s.rfind('é'), Some(14));
739    /// assert_eq!(s.rfind("par"), Some(24));
740    ///
741    /// let x: &[_] = &['1', '2'];
742    /// assert_eq!(s.rfind(x), None);
743    /// ```
744    #[inline]
745    #[must_use]
746    pub fn rfind<P>(&self, mut pat: P) -> Option<usize>
747    where
748        P: JavaStrPattern,
749    {
750        pat.rfind_in(self).map(|(index, _)| index)
751    }
752
753    /// See [`str::rmatch_indices`].
754    ///
755    /// ```
756    /// # use java_string::JavaStr;
757    /// let v: Vec<_> = JavaStr::from_str("abcXXXabcYYYabc")
758    ///     .rmatch_indices("abc")
759    ///     .collect();
760    /// assert_eq!(
761    ///     v,
762    ///     [
763    ///         (12, JavaStr::from_str("abc")),
764    ///         (6, JavaStr::from_str("abc")),
765    ///         (0, JavaStr::from_str("abc"))
766    ///     ]
767    /// );
768    ///
769    /// let v: Vec<_> = JavaStr::from_str("1abcabc2")
770    ///     .rmatch_indices("abc")
771    ///     .collect();
772    /// assert_eq!(
773    ///     v,
774    ///     [(4, JavaStr::from_str("abc")), (1, JavaStr::from_str("abc"))]
775    /// );
776    ///
777    /// let v: Vec<_> = JavaStr::from_str("ababa").rmatch_indices("aba").collect();
778    /// assert_eq!(v, [(2, JavaStr::from_str("aba"))]); // only the last `aba`
779    /// ```
780    #[inline]
781    pub fn rmatch_indices<P>(&self, pat: P) -> RMatchIndices<P>
782    where
783        P: JavaStrPattern,
784    {
785        RMatchIndices {
786            inner: self.match_indices(pat),
787        }
788    }
789
790    /// See [`str::rmatches`].
791    ///
792    /// ```
793    /// # use java_string::{JavaCodePoint, JavaStr};
794    /// let v: Vec<&JavaStr> = JavaStr::from_str("abcXXXabcYYYabc")
795    ///     .rmatches("abc")
796    ///     .collect();
797    /// assert_eq!(
798    ///     v,
799    ///     [
800    ///         JavaStr::from_str("abc"),
801    ///         JavaStr::from_str("abc"),
802    ///         JavaStr::from_str("abc")
803    ///     ]
804    /// );
805    ///
806    /// let v: Vec<&JavaStr> = JavaStr::from_str("1abc2abc3")
807    ///     .rmatches(JavaCodePoint::is_numeric)
808    ///     .collect();
809    /// assert_eq!(
810    ///     v,
811    ///     [
812    ///         JavaStr::from_str("3"),
813    ///         JavaStr::from_str("2"),
814    ///         JavaStr::from_str("1")
815    ///     ]
816    /// );
817    /// ```
818    #[inline]
819    pub fn rmatches<P>(&self, pat: P) -> RMatches<P>
820    where
821        P: JavaStrPattern,
822    {
823        RMatches {
824            inner: self.matches(pat),
825        }
826    }
827
828    /// See [`str::rsplit`].
829    ///
830    /// ```
831    /// # use java_string::JavaStr;
832    /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lamb")
833    ///     .rsplit(' ')
834    ///     .collect();
835    /// assert_eq!(
836    ///     v,
837    ///     [
838    ///         JavaStr::from_str("lamb"),
839    ///         JavaStr::from_str("little"),
840    ///         JavaStr::from_str("a"),
841    ///         JavaStr::from_str("had"),
842    ///         JavaStr::from_str("Mary")
843    ///     ]
844    /// );
845    ///
846    /// let v: Vec<&JavaStr> = JavaStr::from_str("").rsplit('X').collect();
847    /// assert_eq!(v, [JavaStr::from_str("")]);
848    ///
849    /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXXtigerXleopard")
850    ///     .rsplit('X')
851    ///     .collect();
852    /// assert_eq!(
853    ///     v,
854    ///     [
855    ///         JavaStr::from_str("leopard"),
856    ///         JavaStr::from_str("tiger"),
857    ///         JavaStr::from_str(""),
858    ///         JavaStr::from_str("lion")
859    ///     ]
860    /// );
861    ///
862    /// let v: Vec<&JavaStr> = JavaStr::from_str("lion::tiger::leopard")
863    ///     .rsplit("::")
864    ///     .collect();
865    /// assert_eq!(
866    ///     v,
867    ///     [
868    ///         JavaStr::from_str("leopard"),
869    ///         JavaStr::from_str("tiger"),
870    ///         JavaStr::from_str("lion")
871    ///     ]
872    /// );
873    /// ```
874    #[inline]
875    pub fn rsplit<P>(&self, pat: P) -> RSplit<P>
876    where
877        P: JavaStrPattern,
878    {
879        RSplit::new(self, pat)
880    }
881
882    /// See [`str::rsplit_once`].
883    ///
884    /// ```
885    /// # use java_string::JavaStr;
886    /// assert_eq!(JavaStr::from_str("cfg").rsplit_once('='), None);
887    /// assert_eq!(
888    ///     JavaStr::from_str("cfg=foo").rsplit_once('='),
889    ///     Some((JavaStr::from_str("cfg"), JavaStr::from_str("foo")))
890    /// );
891    /// assert_eq!(
892    ///     JavaStr::from_str("cfg=foo=bar").rsplit_once('='),
893    ///     Some((JavaStr::from_str("cfg=foo"), JavaStr::from_str("bar")))
894    /// );
895    /// ```
896    #[inline]
897    #[must_use]
898    pub fn rsplit_once<P>(&self, mut delimiter: P) -> Option<(&JavaStr, &JavaStr)>
899    where
900        P: JavaStrPattern,
901    {
902        let (index, len) = delimiter.rfind_in(self)?;
903        // SAFETY: pattern is known to return valid indices.
904        unsafe {
905            Some((
906                self.get_unchecked(..index),
907                self.get_unchecked(index + len..),
908            ))
909        }
910    }
911
912    /// See [`str::rsplit_terminator`].
913    ///
914    /// ```
915    /// # use java_string::JavaStr;
916    /// let v: Vec<&JavaStr> = JavaStr::from_str("A.B.").rsplit_terminator('.').collect();
917    /// assert_eq!(v, [JavaStr::from_str("B"), JavaStr::from_str("A")]);
918    ///
919    /// let v: Vec<&JavaStr> = JavaStr::from_str("A..B..").rsplit_terminator(".").collect();
920    /// assert_eq!(
921    ///     v,
922    ///     [
923    ///         JavaStr::from_str(""),
924    ///         JavaStr::from_str("B"),
925    ///         JavaStr::from_str(""),
926    ///         JavaStr::from_str("A")
927    ///     ]
928    /// );
929    ///
930    /// let v: Vec<&JavaStr> = JavaStr::from_str("A.B:C.D")
931    ///     .rsplit_terminator(&['.', ':'][..])
932    ///     .collect();
933    /// assert_eq!(
934    ///     v,
935    ///     [
936    ///         JavaStr::from_str("D"),
937    ///         JavaStr::from_str("C"),
938    ///         JavaStr::from_str("B"),
939    ///         JavaStr::from_str("A")
940    ///     ]
941    /// );
942    /// ```
943    #[inline]
944    pub fn rsplit_terminator<P>(&self, pat: P) -> RSplitTerminator<P>
945    where
946        P: JavaStrPattern,
947    {
948        RSplitTerminator::new(self, pat)
949    }
950
951    /// See [`str::rsplitn`].
952    ///
953    /// ```
954    /// # use java_string::JavaStr;
955    /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lamb")
956    ///     .rsplitn(3, ' ')
957    ///     .collect();
958    /// assert_eq!(
959    ///     v,
960    ///     [
961    ///         JavaStr::from_str("lamb"),
962    ///         JavaStr::from_str("little"),
963    ///         JavaStr::from_str("Mary had a")
964    ///     ]
965    /// );
966    ///
967    /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXXtigerXleopard")
968    ///     .rsplitn(3, 'X')
969    ///     .collect();
970    /// assert_eq!(
971    ///     v,
972    ///     [
973    ///         JavaStr::from_str("leopard"),
974    ///         JavaStr::from_str("tiger"),
975    ///         JavaStr::from_str("lionX")
976    ///     ]
977    /// );
978    ///
979    /// let v: Vec<&JavaStr> = JavaStr::from_str("lion::tiger::leopard")
980    ///     .rsplitn(2, "::")
981    ///     .collect();
982    /// assert_eq!(
983    ///     v,
984    ///     [
985    ///         JavaStr::from_str("leopard"),
986    ///         JavaStr::from_str("lion::tiger")
987    ///     ]
988    /// );
989    /// ```
990    #[inline]
991    pub fn rsplitn<P>(&self, n: usize, pat: P) -> RSplitN<P>
992    where
993        P: JavaStrPattern,
994    {
995        RSplitN::new(self, pat, n)
996    }
997
998    /// See [`str::split`].
999    ///
1000    /// ```
1001    /// # use java_string::{JavaCodePoint, JavaStr};
1002    /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lamb")
1003    ///     .split(' ')
1004    ///     .collect();
1005    /// assert_eq!(
1006    ///     v,
1007    ///     [
1008    ///         JavaStr::from_str("Mary"),
1009    ///         JavaStr::from_str("had"),
1010    ///         JavaStr::from_str("a"),
1011    ///         JavaStr::from_str("little"),
1012    ///         JavaStr::from_str("lamb")
1013    ///     ]
1014    /// );
1015    ///
1016    /// let v: Vec<&JavaStr> = JavaStr::from_str("").split('X').collect();
1017    /// assert_eq!(v, [JavaStr::from_str("")]);
1018    ///
1019    /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXXtigerXleopard")
1020    ///     .split('X')
1021    ///     .collect();
1022    /// assert_eq!(
1023    ///     v,
1024    ///     [
1025    ///         JavaStr::from_str("lion"),
1026    ///         JavaStr::from_str(""),
1027    ///         JavaStr::from_str("tiger"),
1028    ///         JavaStr::from_str("leopard")
1029    ///     ]
1030    /// );
1031    ///
1032    /// let v: Vec<&JavaStr> = JavaStr::from_str("lion::tiger::leopard")
1033    ///     .split("::")
1034    ///     .collect();
1035    /// assert_eq!(
1036    ///     v,
1037    ///     [
1038    ///         JavaStr::from_str("lion"),
1039    ///         JavaStr::from_str("tiger"),
1040    ///         JavaStr::from_str("leopard")
1041    ///     ]
1042    /// );
1043    ///
1044    /// let v: Vec<&JavaStr> = JavaStr::from_str("abc1def2ghi")
1045    ///     .split(JavaCodePoint::is_numeric)
1046    ///     .collect();
1047    /// assert_eq!(
1048    ///     v,
1049    ///     [
1050    ///         JavaStr::from_str("abc"),
1051    ///         JavaStr::from_str("def"),
1052    ///         JavaStr::from_str("ghi")
1053    ///     ]
1054    /// );
1055    ///
1056    /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXtigerXleopard")
1057    ///     .split(JavaCodePoint::is_uppercase)
1058    ///     .collect();
1059    /// assert_eq!(
1060    ///     v,
1061    ///     [
1062    ///         JavaStr::from_str("lion"),
1063    ///         JavaStr::from_str("tiger"),
1064    ///         JavaStr::from_str("leopard")
1065    ///     ]
1066    /// );
1067    /// ```
1068    #[inline]
1069    pub fn split<P>(&self, pat: P) -> Split<P>
1070    where
1071        P: JavaStrPattern,
1072    {
1073        Split::new(self, pat)
1074    }
1075
1076    /// See [`str::split_ascii_whitespace`].
1077    ///
1078    /// ```
1079    /// # use java_string::JavaStr;
1080    /// let mut iter = JavaStr::from_str(" Mary   had\ta little  \n\t lamb").split_ascii_whitespace();
1081    /// assert_eq!(Some(JavaStr::from_str("Mary")), iter.next());
1082    /// assert_eq!(Some(JavaStr::from_str("had")), iter.next());
1083    /// assert_eq!(Some(JavaStr::from_str("a")), iter.next());
1084    /// assert_eq!(Some(JavaStr::from_str("little")), iter.next());
1085    /// assert_eq!(Some(JavaStr::from_str("lamb")), iter.next());
1086    ///
1087    /// assert_eq!(None, iter.next());
1088    /// ```
1089    #[inline]
1090    pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> {
1091        #[inline]
1092        fn is_non_empty(bytes: &&[u8]) -> bool {
1093            !bytes.is_empty()
1094        }
1095
1096        SplitAsciiWhitespace {
1097            inner: self
1098                .as_bytes()
1099                .split(u8::is_ascii_whitespace as fn(&u8) -> bool)
1100                .filter(is_non_empty as fn(&&[u8]) -> bool)
1101                .map(|bytes| unsafe { JavaStr::from_semi_utf8_unchecked(bytes) }),
1102        }
1103    }
1104
1105    /// See [`str::split_at`].
1106    ///
1107    /// ```
1108    /// # use java_string::JavaStr;
1109    /// let s = JavaStr::from_str("Per Martin-Löf");
1110    ///
1111    /// let (first, last) = s.split_at(3);
1112    ///
1113    /// assert_eq!("Per", first);
1114    /// assert_eq!(" Martin-Löf", last);
1115    /// ```
1116    /// ```should_panic
1117    /// # use java_string::JavaStr;
1118    /// let s = JavaStr::from_str("Per Martin-Löf");
1119    /// // Should panic
1120    /// let _ = s.split_at(13);
1121    /// ```
1122    #[inline]
1123    #[must_use]
1124    pub fn split_at(&self, mid: usize) -> (&JavaStr, &JavaStr) {
1125        // is_char_boundary checks that the index is in [0, .len()]
1126        if self.is_char_boundary(mid) {
1127            // SAFETY: just checked that `mid` is on a char boundary.
1128            unsafe {
1129                (
1130                    self.get_unchecked(0..mid),
1131                    self.get_unchecked(mid..self.len()),
1132                )
1133            }
1134        } else {
1135            slice_error_fail(self, 0, mid)
1136        }
1137    }
1138
1139    /// See [`str::split_at_mut`].
1140    ///
1141    /// ```
1142    /// # use java_string::{JavaStr, JavaString};
1143    /// let mut s = JavaString::from("Per Martin-Löf");
1144    /// let s = s.as_mut_java_str();
1145    ///
1146    /// let (first, last) = s.split_at_mut(3);
1147    ///
1148    /// assert_eq!("Per", first);
1149    /// assert_eq!(" Martin-Löf", last);
1150    /// ```
1151    /// ```should_panic
1152    /// # use java_string::{JavaStr, JavaString};
1153    /// let mut s = JavaString::from("Per Martin-Löf");
1154    /// let s = s.as_mut_java_str();
1155    /// // Should panic
1156    /// let _ = s.split_at(13);
1157    /// ```
1158    #[inline]
1159    #[must_use]
1160    pub fn split_at_mut(&mut self, mid: usize) -> (&mut JavaStr, &mut JavaStr) {
1161        // is_char_boundary checks that the index is in [0, .len()]
1162        if self.is_char_boundary(mid) {
1163            let len = self.len();
1164            let ptr = self.as_mut_ptr();
1165            // SAFETY: just checked that `mid` is on a char boundary.
1166            unsafe {
1167                (
1168                    JavaStr::from_semi_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)),
1169                    JavaStr::from_semi_utf8_unchecked_mut(slice::from_raw_parts_mut(
1170                        ptr.add(mid),
1171                        len - mid,
1172                    )),
1173                )
1174            }
1175        } else {
1176            slice_error_fail(self, 0, mid)
1177        }
1178    }
1179
1180    /// See [`str::split_inclusive`].
1181    ///
1182    /// ```
1183    /// # use java_string::JavaStr;
1184    /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lamb\nlittle lamb\nlittle lamb.\n")
1185    ///     .split_inclusive('\n')
1186    ///     .collect();
1187    /// assert_eq!(
1188    ///     v,
1189    ///     [
1190    ///         JavaStr::from_str("Mary had a little lamb\n"),
1191    ///         JavaStr::from_str("little lamb\n"),
1192    ///         JavaStr::from_str("little lamb.\n")
1193    ///     ]
1194    /// );
1195    /// ```
1196    #[inline]
1197    pub fn split_inclusive<P>(&self, pat: P) -> SplitInclusive<P>
1198    where
1199        P: JavaStrPattern,
1200    {
1201        SplitInclusive::new(self, pat)
1202    }
1203
1204    /// See [`str::split_once`].
1205    ///
1206    /// ```
1207    /// # use java_string::JavaStr;
1208    /// assert_eq!(JavaStr::from_str("cfg").split_once('='), None);
1209    /// assert_eq!(
1210    ///     JavaStr::from_str("cfg=").split_once('='),
1211    ///     Some((JavaStr::from_str("cfg"), JavaStr::from_str("")))
1212    /// );
1213    /// assert_eq!(
1214    ///     JavaStr::from_str("cfg=foo").split_once('='),
1215    ///     Some((JavaStr::from_str("cfg"), JavaStr::from_str("foo")))
1216    /// );
1217    /// assert_eq!(
1218    ///     JavaStr::from_str("cfg=foo=bar").split_once('='),
1219    ///     Some((JavaStr::from_str("cfg"), JavaStr::from_str("foo=bar")))
1220    /// );
1221    /// ```
1222    #[inline]
1223    #[must_use]
1224    pub fn split_once<P>(&self, mut delimiter: P) -> Option<(&JavaStr, &JavaStr)>
1225    where
1226        P: JavaStrPattern,
1227    {
1228        let (index, len) = delimiter.find_in(self)?;
1229        // SAFETY: pattern is known to return valid indices.
1230        unsafe {
1231            Some((
1232                self.get_unchecked(..index),
1233                self.get_unchecked(index + len..),
1234            ))
1235        }
1236    }
1237
1238    /// See [`str::split_terminator`].
1239    ///
1240    /// ```
1241    /// # use java_string::JavaStr;
1242    /// let v: Vec<&JavaStr> = JavaStr::from_str("A.B.").split_terminator('.').collect();
1243    /// assert_eq!(v, [JavaStr::from_str("A"), JavaStr::from_str("B")]);
1244    ///
1245    /// let v: Vec<&JavaStr> = JavaStr::from_str("A..B..").split_terminator(".").collect();
1246    /// assert_eq!(
1247    ///     v,
1248    ///     [
1249    ///         JavaStr::from_str("A"),
1250    ///         JavaStr::from_str(""),
1251    ///         JavaStr::from_str("B"),
1252    ///         JavaStr::from_str("")
1253    ///     ]
1254    /// );
1255    ///
1256    /// let v: Vec<&JavaStr> = JavaStr::from_str("A.B:C.D")
1257    ///     .split_terminator(&['.', ':'][..])
1258    ///     .collect();
1259    /// assert_eq!(
1260    ///     v,
1261    ///     [
1262    ///         JavaStr::from_str("A"),
1263    ///         JavaStr::from_str("B"),
1264    ///         JavaStr::from_str("C"),
1265    ///         JavaStr::from_str("D")
1266    ///     ]
1267    /// );
1268    /// ```
1269    #[inline]
1270    pub fn split_terminator<P>(&self, pat: P) -> SplitTerminator<P>
1271    where
1272        P: JavaStrPattern,
1273    {
1274        SplitTerminator::new(self, pat)
1275    }
1276
1277    /// See [`str::split_whitespace`].
1278    #[inline]
1279    pub fn split_whitespace(&self) -> SplitWhitespace<'_> {
1280        SplitWhitespace {
1281            inner: self
1282                .split(JavaCodePoint::is_whitespace as fn(JavaCodePoint) -> bool)
1283                .filter(|str| !str.is_empty()),
1284        }
1285    }
1286
1287    /// See [`str::splitn`].
1288    ///
1289    /// ```
1290    /// # use java_string::JavaStr;
1291    /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lambda")
1292    ///     .splitn(3, ' ')
1293    ///     .collect();
1294    /// assert_eq!(
1295    ///     v,
1296    ///     [
1297    ///         JavaStr::from_str("Mary"),
1298    ///         JavaStr::from_str("had"),
1299    ///         JavaStr::from_str("a little lambda")
1300    ///     ]
1301    /// );
1302    ///
1303    /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXXtigerXleopard")
1304    ///     .splitn(3, "X")
1305    ///     .collect();
1306    /// assert_eq!(
1307    ///     v,
1308    ///     [
1309    ///         JavaStr::from_str("lion"),
1310    ///         JavaStr::from_str(""),
1311    ///         JavaStr::from_str("tigerXleopard")
1312    ///     ]
1313    /// );
1314    ///
1315    /// let v: Vec<&JavaStr> = JavaStr::from_str("abcXdef").splitn(1, 'X').collect();
1316    /// assert_eq!(v, [JavaStr::from_str("abcXdef")]);
1317    ///
1318    /// let v: Vec<&JavaStr> = JavaStr::from_str("").splitn(1, 'X').collect();
1319    /// assert_eq!(v, [JavaStr::from_str("")]);
1320    /// ```
1321    #[inline]
1322    pub fn splitn<P>(&self, n: usize, pat: P) -> SplitN<P>
1323    where
1324        P: JavaStrPattern,
1325    {
1326        SplitN::new(self, pat, n)
1327    }
1328
1329    /// See [`str::starts_with`].
1330    ///
1331    /// ```
1332    /// # use java_string::JavaStr;
1333    /// let bananas = JavaStr::from_str("bananas");
1334    ///
1335    /// assert!(bananas.starts_with("bana"));
1336    /// assert!(!bananas.starts_with("nana"));
1337    /// ```
1338    #[inline]
1339    #[must_use]
1340    pub fn starts_with<P>(&self, mut pat: P) -> bool
1341    where
1342        P: JavaStrPattern,
1343    {
1344        pat.prefix_len_in(self).is_some()
1345    }
1346
1347    /// See [`str::strip_prefix`].
1348    ///
1349    /// ```
1350    /// # use java_string::JavaStr;
1351    /// assert_eq!(
1352    ///     JavaStr::from_str("foo:bar").strip_prefix("foo:"),
1353    ///     Some(JavaStr::from_str("bar"))
1354    /// );
1355    /// assert_eq!(JavaStr::from_str("foo:bar").strip_prefix("bar"), None);
1356    /// assert_eq!(
1357    ///     JavaStr::from_str("foofoo").strip_prefix("foo"),
1358    ///     Some(JavaStr::from_str("foo"))
1359    /// );
1360    /// ```
1361    #[inline]
1362    #[must_use]
1363    pub fn strip_prefix<P>(&self, mut prefix: P) -> Option<&JavaStr>
1364    where
1365        P: JavaStrPattern,
1366    {
1367        let len = prefix.prefix_len_in(self)?;
1368        // SAFETY: pattern is known to return valid indices.
1369        unsafe { Some(self.get_unchecked(len..)) }
1370    }
1371
1372    /// See [`str::strip_suffix`].
1373    ///
1374    /// ```
1375    /// # use java_string::JavaStr;
1376    /// assert_eq!(
1377    ///     JavaStr::from_str("bar:foo").strip_suffix(":foo"),
1378    ///     Some(JavaStr::from_str("bar"))
1379    /// );
1380    /// assert_eq!(JavaStr::from_str("bar:foo").strip_suffix("bar"), None);
1381    /// assert_eq!(
1382    ///     JavaStr::from_str("foofoo").strip_suffix("foo"),
1383    ///     Some(JavaStr::from_str("foo"))
1384    /// );
1385    /// ```
1386    #[inline]
1387    #[must_use]
1388    pub fn strip_suffix<P>(&self, mut suffix: P) -> Option<&JavaStr>
1389    where
1390        P: JavaStrPattern,
1391    {
1392        let len = suffix.suffix_len_in(self)?;
1393        // SAFETY: pattern is known to return valid indices.
1394        unsafe { Some(self.get_unchecked(..self.len() - len)) }
1395    }
1396
1397    /// See [`str::to_ascii_lowercase`].
1398    #[inline]
1399    #[must_use]
1400    pub fn to_ascii_lowercase(&self) -> JavaString {
1401        let mut s = self.to_owned();
1402        s.make_ascii_lowercase();
1403        s
1404    }
1405
1406    /// See [`str::to_ascii_uppercase`].
1407    #[inline]
1408    #[must_use]
1409    pub fn to_ascii_uppercase(&self) -> JavaString {
1410        let mut s = self.to_owned();
1411        s.make_ascii_uppercase();
1412        s
1413    }
1414
1415    /// See [`str::to_lowercase`].
1416    ///
1417    /// ```
1418    /// # use java_string::{JavaCodePoint, JavaStr, JavaString};
1419    /// let s = JavaStr::from_str("HELLO");
1420    /// assert_eq!("hello", s.to_lowercase());
1421    ///
1422    /// let odysseus = JavaStr::from_str("ὈΔΥΣΣΕΎΣ");
1423    /// assert_eq!("ὀδυσσεύς", odysseus.to_lowercase());
1424    ///
1425    /// let s = JavaString::from("Hello ")
1426    ///     + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1427    ///     + JavaStr::from_str(" World!");
1428    /// let expected = JavaString::from("hello ")
1429    ///     + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1430    ///     + JavaStr::from_str(" world!");
1431    /// assert_eq!(expected, s.to_lowercase());
1432    /// ```
1433    #[inline]
1434    #[must_use]
1435    pub fn to_lowercase(&self) -> JavaString {
1436        self.transform_string(str::to_lowercase, |ch| ch)
1437    }
1438
1439    /// See [`str::to_uppercase`].
1440    ///
1441    /// ```
1442    /// # use java_string::{JavaCodePoint, JavaStr, JavaString};
1443    /// let s = JavaStr::from_str("hello");
1444    /// assert_eq!("HELLO", s.to_uppercase());
1445    ///
1446    /// let s = JavaStr::from_str("tschüß");
1447    /// assert_eq!("TSCHÜSS", s.to_uppercase());
1448    ///
1449    /// let s = JavaString::from("Hello ")
1450    ///     + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1451    ///     + JavaStr::from_str(" World!");
1452    /// let expected = JavaString::from("HELLO ")
1453    ///     + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1454    ///     + JavaStr::from_str(" WORLD!");
1455    /// assert_eq!(expected, s.to_uppercase());
1456    /// ```
1457    #[inline]
1458    #[must_use]
1459    pub fn to_uppercase(&self) -> JavaString {
1460        self.transform_string(str::to_uppercase, |ch| ch)
1461    }
1462
1463    /// See [`str::trim`].
1464    #[inline]
1465    #[must_use]
1466    pub fn trim(&self) -> &JavaStr {
1467        self.trim_matches(|c: JavaCodePoint| c.is_whitespace())
1468    }
1469
1470    /// See [`str::trim_end`].
1471    #[inline]
1472    #[must_use]
1473    pub fn trim_end(&self) -> &JavaStr {
1474        self.trim_end_matches(|c: JavaCodePoint| c.is_whitespace())
1475    }
1476
1477    /// See [`str::trim_end_matches`].
1478    ///
1479    /// ```
1480    /// # use java_string::{JavaCodePoint, JavaStr};
1481    /// assert_eq!(
1482    ///     JavaStr::from_str("11foo1bar11").trim_end_matches('1'),
1483    ///     "11foo1bar"
1484    /// );
1485    /// assert_eq!(
1486    ///     JavaStr::from_str("123foo1bar123").trim_end_matches(JavaCodePoint::is_numeric),
1487    ///     "123foo1bar"
1488    /// );
1489    ///
1490    /// let x: &[_] = &['1', '2'];
1491    /// assert_eq!(
1492    ///     JavaStr::from_str("12foo1bar12").trim_end_matches(x),
1493    ///     "12foo1bar"
1494    /// );
1495    /// ```
1496    #[inline]
1497    #[must_use]
1498    pub fn trim_end_matches<P>(&self, mut pat: P) -> &JavaStr
1499    where
1500        P: JavaStrPattern,
1501    {
1502        let mut str = self;
1503        while let Some(suffix_len) = pat.suffix_len_in(str) {
1504            if suffix_len == 0 {
1505                break;
1506            }
1507            // SAFETY: pattern is known to return valid indices.
1508            str = unsafe { str.get_unchecked(..str.len() - suffix_len) };
1509        }
1510        str
1511    }
1512
1513    /// See [`str::trim_matches`].
1514    ///
1515    /// ```
1516    /// # use java_string::{JavaCodePoint, JavaStr};
1517    /// assert_eq!(
1518    ///     JavaStr::from_str("11foo1bar11").trim_matches('1'),
1519    ///     "foo1bar"
1520    /// );
1521    /// assert_eq!(
1522    ///     JavaStr::from_str("123foo1bar123").trim_matches(JavaCodePoint::is_numeric),
1523    ///     "foo1bar"
1524    /// );
1525    ///
1526    /// let x: &[_] = &['1', '2'];
1527    /// assert_eq!(JavaStr::from_str("12foo1bar12").trim_matches(x), "foo1bar");
1528    /// ```
1529    #[inline]
1530    #[must_use]
1531    pub fn trim_matches<P>(&self, mut pat: P) -> &JavaStr
1532    where
1533        P: JavaStrPattern,
1534    {
1535        let mut str = self;
1536        while let Some(prefix_len) = pat.prefix_len_in(str) {
1537            if prefix_len == 0 {
1538                break;
1539            }
1540            // SAFETY: pattern is known to return valid indices.
1541            str = unsafe { str.get_unchecked(prefix_len..) };
1542        }
1543        while let Some(suffix_len) = pat.suffix_len_in(str) {
1544            if suffix_len == 0 {
1545                break;
1546            }
1547            // SAFETY: pattern is known to return valid indices.
1548            str = unsafe { str.get_unchecked(..str.len() - suffix_len) };
1549        }
1550        str
1551    }
1552
1553    /// See [`str::trim_start`].
1554    #[inline]
1555    #[must_use]
1556    pub fn trim_start(&self) -> &JavaStr {
1557        self.trim_start_matches(|c: JavaCodePoint| c.is_whitespace())
1558    }
1559
1560    /// See [`str::trim_start_matches`].
1561    ///
1562    /// ```
1563    /// # use java_string::{JavaCodePoint, JavaStr};
1564    /// assert_eq!(
1565    ///     JavaStr::from_str("11foo1bar11").trim_start_matches('1'),
1566    ///     "foo1bar11"
1567    /// );
1568    /// assert_eq!(
1569    ///     JavaStr::from_str("123foo1bar123").trim_start_matches(JavaCodePoint::is_numeric),
1570    ///     "foo1bar123"
1571    /// );
1572    ///
1573    /// let x: &[_] = &['1', '2'];
1574    /// assert_eq!(
1575    ///     JavaStr::from_str("12foo1bar12").trim_start_matches(x),
1576    ///     "foo1bar12"
1577    /// );
1578    /// ```
1579    #[inline]
1580    #[must_use]
1581    pub fn trim_start_matches<P>(&self, mut pat: P) -> &JavaStr
1582    where
1583        P: JavaStrPattern,
1584    {
1585        let mut str = self;
1586        while let Some(prefix_len) = pat.prefix_len_in(str) {
1587            if prefix_len == 0 {
1588                break;
1589            }
1590            // SAFETY: pattern is known to return valid indices.
1591            str = unsafe { str.get_unchecked(prefix_len..) };
1592        }
1593        str
1594    }
1595
1596    #[inline]
1597    fn transform_string<SF, ICF>(
1598        &self,
1599        mut string_transformer: SF,
1600        invalid_char_transformer: ICF,
1601    ) -> JavaString
1602    where
1603        SF: FnMut(&str) -> String,
1604        ICF: FnMut(&JavaStr) -> &JavaStr,
1605    {
1606        let bytes = self.as_bytes();
1607        match run_utf8_full_validation_from_semi(bytes) {
1608            Ok(()) => JavaString::from(string_transformer(unsafe {
1609                // SAFETY: validation succeeded
1610                std::str::from_utf8_unchecked(bytes)
1611            })),
1612            Err(error) => {
1613                self.transform_invalid_string(error, string_transformer, invalid_char_transformer)
1614            }
1615        }
1616    }
1617
1618    #[inline]
1619    fn transform_invalid_string<SF, ICF>(
1620        &self,
1621        error: Utf8Error,
1622        mut string_transformer: SF,
1623        mut invalid_char_transformer: ICF,
1624    ) -> JavaString
1625    where
1626        SF: FnMut(&str) -> String,
1627        ICF: FnMut(&JavaStr) -> &JavaStr,
1628    {
1629        let bytes = self.as_bytes();
1630        let mut result = JavaString::from(string_transformer(unsafe {
1631            // SAFETY: validation succeeded up to this index
1632            std::str::from_utf8_unchecked(bytes.get_unchecked(..error.valid_up_to))
1633        }));
1634        result.push_java_str(invalid_char_transformer(unsafe {
1635            // SAFETY: any UTF-8 error in semi-valid UTF-8 is a 3 byte long sequence
1636            // representing a surrogate code point. We're pushing that sequence now
1637            JavaStr::from_semi_utf8_unchecked(
1638                bytes.get_unchecked(error.valid_up_to..error.valid_up_to + 3),
1639            )
1640        }));
1641        let mut index = error.valid_up_to + 3;
1642        loop {
1643            let remainder = unsafe { bytes.get_unchecked(index..) };
1644            match run_utf8_full_validation_from_semi(remainder) {
1645                Ok(()) => {
1646                    result.push_str(&string_transformer(unsafe {
1647                        // SAFETY: validation succeeded
1648                        std::str::from_utf8_unchecked(remainder)
1649                    }));
1650                    return result;
1651                }
1652                Err(error) => {
1653                    result.push_str(&string_transformer(unsafe {
1654                        // SAFETY: validation succeeded up to this index
1655                        std::str::from_utf8_unchecked(
1656                            bytes.get_unchecked(index..index + error.valid_up_to),
1657                        )
1658                    }));
1659                    result.push_java_str(invalid_char_transformer(unsafe {
1660                        // SAFETY: see comment above
1661                        JavaStr::from_semi_utf8_unchecked(bytes.get_unchecked(
1662                            index + error.valid_up_to..index + error.valid_up_to + 3,
1663                        ))
1664                    }));
1665                    index += error.valid_up_to + 3;
1666                }
1667            }
1668        }
1669    }
1670}
1671
1672impl<'a> Add<&JavaStr> for Cow<'a, JavaStr> {
1673    type Output = Cow<'a, JavaStr>;
1674
1675    #[inline]
1676    fn add(mut self, rhs: &JavaStr) -> Self::Output {
1677        self += rhs;
1678        self
1679    }
1680}
1681
1682impl AddAssign<&JavaStr> for Cow<'_, JavaStr> {
1683    #[inline]
1684    fn add_assign(&mut self, rhs: &JavaStr) {
1685        if !rhs.is_empty() {
1686            match self {
1687                Cow::Borrowed(lhs) => {
1688                    let mut result = lhs.to_owned();
1689                    result.push_java_str(rhs);
1690                    *self = Cow::Owned(result);
1691                }
1692                Cow::Owned(lhs) => {
1693                    lhs.push_java_str(rhs);
1694                }
1695            }
1696        }
1697    }
1698}
1699
1700impl AsRef<[u8]> for JavaStr {
1701    #[inline]
1702    fn as_ref(&self) -> &[u8] {
1703        self.as_bytes()
1704    }
1705}
1706
1707impl AsRef<JavaStr> for str {
1708    #[inline]
1709    fn as_ref(&self) -> &JavaStr {
1710        JavaStr::from_str(self)
1711    }
1712}
1713
1714impl AsRef<JavaStr> for String {
1715    #[inline]
1716    fn as_ref(&self) -> &JavaStr {
1717        JavaStr::from_str(self)
1718    }
1719}
1720
1721impl AsRef<JavaStr> for JavaStr {
1722    #[inline]
1723    fn as_ref(&self) -> &JavaStr {
1724        self
1725    }
1726}
1727
1728impl Clone for Box<JavaStr> {
1729    #[inline]
1730    fn clone(&self) -> Self {
1731        let buf: Box<[u8]> = self.as_bytes().into();
1732        unsafe { JavaStr::from_boxed_semi_utf8_unchecked(buf) }
1733    }
1734}
1735
1736impl Debug for JavaStr {
1737    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1738        f.write_char('"')?;
1739        let mut from = 0;
1740        for (i, c) in self.char_indices() {
1741            let esc = c.escape_debug_ext(EscapeDebugExtArgs {
1742                escape_single_quote: false,
1743                escape_double_quote: true,
1744            });
1745            // If char needs escaping, flush backlog so far and write, else skip.
1746            // Also handle invalid UTF-8 here
1747            if esc.len() != 1 || c.as_char().is_none() {
1748                unsafe {
1749                    // SAFETY: any invalid UTF-8 should have been caught by a previous iteration
1750                    f.write_str(self[from..i].as_str_unchecked())?
1751                };
1752                for c in esc {
1753                    f.write_char(c)?;
1754                }
1755                from = i + c.len_utf8();
1756            }
1757        }
1758        unsafe {
1759            // SAFETY: any invalid UTF-8 should have been caught by the loop above
1760            f.write_str(self[from..].as_str_unchecked())?
1761        };
1762        f.write_char('"')
1763    }
1764}
1765
1766impl Default for &JavaStr {
1767    #[inline]
1768    fn default() -> Self {
1769        JavaStr::from_str("")
1770    }
1771}
1772
1773impl Default for Box<JavaStr> {
1774    #[inline]
1775    fn default() -> Self {
1776        JavaStr::from_boxed_str(Box::<str>::default())
1777    }
1778}
1779
1780impl Display for JavaStr {
1781    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1782        Display::fmt(&self.as_str_lossy(), f)
1783    }
1784}
1785
1786impl<'a> From<&'a JavaStr> for Cow<'a, JavaStr> {
1787    #[inline]
1788    fn from(value: &'a JavaStr) -> Self {
1789        Cow::Borrowed(value)
1790    }
1791}
1792
1793impl From<&JavaStr> for Arc<JavaStr> {
1794    #[inline]
1795    fn from(value: &JavaStr) -> Self {
1796        let arc = Arc::<[u8]>::from(value.as_bytes());
1797        unsafe { Arc::from_raw(Arc::into_raw(arc) as *const JavaStr) }
1798    }
1799}
1800
1801impl From<&JavaStr> for Box<JavaStr> {
1802    #[inline]
1803    fn from(value: &JavaStr) -> Self {
1804        unsafe { JavaStr::from_boxed_semi_utf8_unchecked(Box::from(value.as_bytes())) }
1805    }
1806}
1807
1808impl From<&JavaStr> for Rc<JavaStr> {
1809    #[inline]
1810    fn from(value: &JavaStr) -> Self {
1811        let rc = Rc::<[u8]>::from(value.as_bytes());
1812        unsafe { Rc::from_raw(Rc::into_raw(rc) as *const JavaStr) }
1813    }
1814}
1815
1816impl From<&JavaStr> for Vec<u8> {
1817    #[inline]
1818    fn from(value: &JavaStr) -> Self {
1819        From::from(value.as_bytes())
1820    }
1821}
1822
1823impl From<Cow<'_, JavaStr>> for Box<JavaStr> {
1824    #[inline]
1825    fn from(value: Cow<'_, JavaStr>) -> Self {
1826        match value {
1827            Cow::Borrowed(s) => Box::from(s),
1828            Cow::Owned(s) => Box::from(s),
1829        }
1830    }
1831}
1832
1833impl From<JavaString> for Box<JavaStr> {
1834    #[inline]
1835    fn from(value: JavaString) -> Self {
1836        value.into_boxed_str()
1837    }
1838}
1839
1840impl<'a> From<&'a str> for &'a JavaStr {
1841    #[inline]
1842    fn from(value: &'a str) -> Self {
1843        JavaStr::from_str(value)
1844    }
1845}
1846
1847impl<'a> From<&'a String> for &'a JavaStr {
1848    #[inline]
1849    fn from(value: &'a String) -> Self {
1850        JavaStr::from_str(value)
1851    }
1852}
1853
1854impl Hash for JavaStr {
1855    #[inline]
1856    fn hash<H: Hasher>(&self, state: &mut H) {
1857        state.write(self.as_bytes());
1858        state.write_u8(0xff);
1859    }
1860}
1861
1862impl<I> Index<I> for JavaStr
1863where
1864    I: JavaStrSliceIndex,
1865{
1866    type Output = JavaStr;
1867
1868    #[inline]
1869    fn index(&self, index: I) -> &Self::Output {
1870        index.index(self)
1871    }
1872}
1873
1874impl<I> IndexMut<I> for JavaStr
1875where
1876    I: JavaStrSliceIndex,
1877{
1878    #[inline]
1879    fn index_mut(&mut self, index: I) -> &mut Self::Output {
1880        index.index_mut(self)
1881    }
1882}
1883
1884impl<'b> PartialEq<&'b JavaStr> for Cow<'_, str> {
1885    #[inline]
1886    fn eq(&self, other: &&'b JavaStr) -> bool {
1887        self == *other
1888    }
1889}
1890
1891impl<'b> PartialEq<&'b JavaStr> for Cow<'_, JavaStr> {
1892    #[inline]
1893    fn eq(&self, other: &&'b JavaStr) -> bool {
1894        self == *other
1895    }
1896}
1897
1898impl<'a> PartialEq<Cow<'a, str>> for &JavaStr {
1899    #[inline]
1900    fn eq(&self, other: &Cow<'a, str>) -> bool {
1901        *self == other
1902    }
1903}
1904
1905impl<'a> PartialEq<Cow<'a, str>> for JavaStr {
1906    #[inline]
1907    fn eq(&self, other: &Cow<'a, str>) -> bool {
1908        other == self
1909    }
1910}
1911
1912impl<'a> PartialEq<Cow<'a, JavaStr>> for &JavaStr {
1913    #[inline]
1914    fn eq(&self, other: &Cow<'a, JavaStr>) -> bool {
1915        *self == other
1916    }
1917}
1918
1919impl<'a> PartialEq<Cow<'a, JavaStr>> for JavaStr {
1920    #[inline]
1921    fn eq(&self, other: &Cow<'a, JavaStr>) -> bool {
1922        other == self
1923    }
1924}
1925
1926impl PartialEq<String> for &JavaStr {
1927    #[inline]
1928    fn eq(&self, other: &String) -> bool {
1929        *self == other
1930    }
1931}
1932
1933impl PartialEq<String> for JavaStr {
1934    #[inline]
1935    fn eq(&self, other: &String) -> bool {
1936        self == &other[..]
1937    }
1938}
1939
1940impl PartialEq<JavaStr> for String {
1941    #[inline]
1942    fn eq(&self, other: &JavaStr) -> bool {
1943        &self[..] == other
1944    }
1945}
1946
1947impl PartialEq<JavaString> for &JavaStr {
1948    #[inline]
1949    fn eq(&self, other: &JavaString) -> bool {
1950        *self == other
1951    }
1952}
1953
1954impl PartialEq<JavaString> for JavaStr {
1955    #[inline]
1956    fn eq(&self, other: &JavaString) -> bool {
1957        self == other[..]
1958    }
1959}
1960
1961impl PartialEq<JavaStr> for Cow<'_, str> {
1962    #[inline]
1963    fn eq(&self, other: &JavaStr) -> bool {
1964        match self {
1965            Cow::Borrowed(this) => this == other,
1966            Cow::Owned(this) => this == other,
1967        }
1968    }
1969}
1970
1971impl PartialEq<JavaStr> for Cow<'_, JavaStr> {
1972    #[inline]
1973    fn eq(&self, other: &JavaStr) -> bool {
1974        match self {
1975            Cow::Borrowed(this) => this == other,
1976            Cow::Owned(this) => this == other,
1977        }
1978    }
1979}
1980
1981impl PartialEq<JavaStr> for str {
1982    #[inline]
1983    fn eq(&self, other: &JavaStr) -> bool {
1984        JavaStr::from_str(self) == other
1985    }
1986}
1987
1988impl PartialEq<JavaStr> for &str {
1989    #[inline]
1990    fn eq(&self, other: &JavaStr) -> bool {
1991        self.as_bytes() == &other.inner
1992    }
1993}
1994
1995impl PartialEq<str> for JavaStr {
1996    #[inline]
1997    fn eq(&self, other: &str) -> bool {
1998        &self.inner == other.as_bytes()
1999    }
2000}
2001
2002impl<'a> PartialEq<&'a str> for JavaStr {
2003    #[inline]
2004    fn eq(&self, other: &&'a str) -> bool {
2005        &self.inner == other.as_bytes()
2006    }
2007}
2008
2009impl PartialEq<JavaStr> for &JavaStr {
2010    #[inline]
2011    fn eq(&self, other: &JavaStr) -> bool {
2012        self.inner == other.inner
2013    }
2014}
2015
2016impl<'a> PartialEq<&'a JavaStr> for JavaStr {
2017    #[inline]
2018    fn eq(&self, other: &&'a JavaStr) -> bool {
2019        self.inner == other.inner
2020    }
2021}
2022
2023impl ToOwned for JavaStr {
2024    type Owned = JavaString;
2025
2026    #[inline]
2027    fn to_owned(&self) -> Self::Owned {
2028        unsafe { JavaString::from_semi_utf8_unchecked(self.as_bytes().to_vec()) }
2029    }
2030}
2031
2032mod private_slice_index {
2033    use std::ops;
2034
2035    pub trait Sealed {}
2036
2037    impl Sealed for ops::Range<usize> {}
2038    impl Sealed for ops::RangeTo<usize> {}
2039    impl Sealed for ops::RangeFrom<usize> {}
2040    impl Sealed for ops::RangeFull {}
2041    impl Sealed for ops::RangeInclusive<usize> {}
2042    impl Sealed for ops::RangeToInclusive<usize> {}
2043}
2044
2045/// # Safety
2046///
2047/// Implementations' `check_bounds` method must properly check the bounds of the
2048/// slice, such that calling `get_unchecked` is not UB.
2049pub unsafe trait JavaStrSliceIndex: private_slice_index::Sealed + Sized {
2050    fn check_bounds(&self, slice: &JavaStr) -> bool;
2051    fn check_bounds_fail(self, slice: &JavaStr) -> !;
2052
2053    /// # Safety
2054    ///
2055    /// - The input slice must be a valid pointer
2056    /// - This index must not be out of bounds of the input slice
2057    /// - The indices of this slice must point to char boundaries in the input
2058    ///   slice
2059    unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr;
2060
2061    /// # Safety
2062    ///
2063    /// - The input slice must be a valid pointer
2064    /// - This index must not be out of bounds of the input slice
2065    /// - The indices of this slice must point to char boundaries in the input
2066    ///   slice
2067    unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr;
2068
2069    #[inline]
2070    fn get(self, slice: &JavaStr) -> Option<&JavaStr> {
2071        self.check_bounds(slice)
2072            .then(|| unsafe { &*self.get_unchecked(slice) })
2073    }
2074
2075    #[inline]
2076    fn get_mut(self, slice: &mut JavaStr) -> Option<&mut JavaStr> {
2077        self.check_bounds(slice)
2078            .then(|| unsafe { &mut *self.get_unchecked_mut(slice) })
2079    }
2080
2081    #[inline]
2082    fn index(self, slice: &JavaStr) -> &JavaStr {
2083        if self.check_bounds(slice) {
2084            unsafe { &*self.get_unchecked(slice) }
2085        } else {
2086            self.check_bounds_fail(slice)
2087        }
2088    }
2089
2090    #[inline]
2091    fn index_mut(self, slice: &mut JavaStr) -> &mut JavaStr {
2092        if self.check_bounds(slice) {
2093            unsafe { &mut *self.get_unchecked_mut(slice) }
2094        } else {
2095            self.check_bounds_fail(slice)
2096        }
2097    }
2098}
2099
2100unsafe impl JavaStrSliceIndex for RangeFull {
2101    #[inline]
2102    fn check_bounds(&self, _slice: &JavaStr) -> bool {
2103        true
2104    }
2105
2106    #[inline]
2107    fn check_bounds_fail(self, _slice: &JavaStr) -> ! {
2108        unreachable!()
2109    }
2110
2111    #[inline]
2112    unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2113        slice
2114    }
2115
2116    #[inline]
2117    unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2118        slice
2119    }
2120}
2121
2122unsafe impl JavaStrSliceIndex for Range<usize> {
2123    #[inline]
2124    fn check_bounds(&self, slice: &JavaStr) -> bool {
2125        self.start <= self.end
2126            && slice.is_char_boundary(self.start)
2127            && slice.is_char_boundary(self.end)
2128    }
2129
2130    #[inline]
2131    #[track_caller]
2132    fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2133        slice_error_fail(slice, self.start, self.end)
2134    }
2135
2136    #[inline]
2137    unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2138        let slice = slice as *const [u8];
2139        // SAFETY: the caller guarantees that `self` is in bounds of `slice`
2140        // which satisfies all the conditions for `add`.
2141        let ptr = unsafe { (slice as *const u8).add(self.start) };
2142        let len = self.end - self.start;
2143        ptr::slice_from_raw_parts(ptr, len) as *const JavaStr
2144    }
2145
2146    #[inline]
2147    unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2148        let slice = slice as *mut [u8];
2149        // SAFETY: see comments for `get_unchecked`.
2150        let ptr = unsafe { (slice as *mut u8).add(self.start) };
2151        let len = self.end - self.start;
2152        ptr::slice_from_raw_parts_mut(ptr, len) as *mut JavaStr
2153    }
2154}
2155
2156unsafe impl JavaStrSliceIndex for RangeTo<usize> {
2157    #[inline]
2158    fn check_bounds(&self, slice: &JavaStr) -> bool {
2159        slice.is_char_boundary(self.end)
2160    }
2161
2162    #[inline]
2163    #[track_caller]
2164    fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2165        slice_error_fail(slice, 0, self.end)
2166    }
2167
2168    #[inline]
2169    unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2170        unsafe { (0..self.end).get_unchecked(slice) }
2171    }
2172
2173    #[inline]
2174    unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2175        unsafe { (0..self.end).get_unchecked_mut(slice) }
2176    }
2177}
2178
2179unsafe impl JavaStrSliceIndex for RangeFrom<usize> {
2180    #[inline]
2181    fn check_bounds(&self, slice: &JavaStr) -> bool {
2182        slice.is_char_boundary(self.start)
2183    }
2184
2185    #[inline]
2186    #[track_caller]
2187    fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2188        slice_error_fail(slice, self.start, slice.len())
2189    }
2190
2191    #[inline]
2192    unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2193        #[allow(clippy::needless_borrow)]
2194        let len = unsafe { (&(*(slice as *const [u8]))).len() };
2195        unsafe { (self.start..len).get_unchecked(slice) }
2196    }
2197
2198    #[inline]
2199    unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2200        #[allow(clippy::needless_borrow)]
2201        let len = unsafe { (&(*(slice as *mut [u8]))).len() };
2202        unsafe { (self.start..len).get_unchecked_mut(slice) }
2203    }
2204}
2205
2206#[inline]
2207fn into_slice_range(range: RangeInclusive<usize>) -> Range<usize> {
2208    let exclusive_end = *range.end() + 1;
2209    let start = match range.end_bound() {
2210        Bound::Excluded(..) => exclusive_end, // excluded
2211        Bound::Included(..) => *range.start(),
2212        Bound::Unbounded => unreachable!(),
2213    };
2214    start..exclusive_end
2215}
2216
2217unsafe impl JavaStrSliceIndex for RangeInclusive<usize> {
2218    #[inline]
2219    fn check_bounds(&self, slice: &JavaStr) -> bool {
2220        *self.end() != usize::MAX && into_slice_range(self.clone()).check_bounds(slice)
2221    }
2222
2223    #[inline]
2224    #[track_caller]
2225    fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2226        if *self.end() == usize::MAX {
2227            str_end_index_overflow_fail()
2228        } else {
2229            into_slice_range(self).check_bounds_fail(slice)
2230        }
2231    }
2232
2233    #[inline]
2234    unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2235        into_slice_range(self).get_unchecked(slice)
2236    }
2237
2238    #[inline]
2239    unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2240        into_slice_range(self).get_unchecked_mut(slice)
2241    }
2242}
2243
2244unsafe impl JavaStrSliceIndex for RangeToInclusive<usize> {
2245    #[inline]
2246    fn check_bounds(&self, slice: &JavaStr) -> bool {
2247        (0..=self.end).check_bounds(slice)
2248    }
2249
2250    #[inline]
2251    fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2252        (0..=self.end).check_bounds_fail(slice)
2253    }
2254
2255    #[inline]
2256    unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2257        (0..=self.end).get_unchecked(slice)
2258    }
2259
2260    #[inline]
2261    unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2262        (0..=self.end).get_unchecked_mut(slice)
2263    }
2264}