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}