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 const 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 const 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 const 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 const 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 const 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 const 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 if index >= self.len() {
486 // For `true` 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 PR https://github.com/rust-lang/rust/pull/84751 for more details.
493 index == self.len()
494 } else {
495 // This is bit magic equivalent to: b < 128 || b >= 192
496 (self.as_bytes()[index] as i8) >= -0x40
497 }
498 }
499
500 pub(crate) fn floor_char_boundary(&self, index: usize) -> usize {
501 if index >= self.len() {
502 self.len()
503 } else {
504 let lower_bound = index.saturating_sub(3);
505 let new_index = self.as_bytes()[lower_bound..=index].iter().rposition(|b| {
506 // This is bit magic equivalent to: b < 128 || b >= 192
507 (*b as i8) >= -0x40
508 });
509
510 // SAFETY: we know that the character boundary will be within four bytes
511 unsafe { lower_bound + new_index.unwrap_unchecked() }
512 }
513 }
514
515 /// See [`str::is_empty`].
516 #[inline]
517 #[must_use]
518 pub fn is_empty(&self) -> bool {
519 self.len() == 0
520 }
521
522 /// See [`str::len`].
523 #[inline]
524 #[must_use]
525 pub const fn len(&self) -> usize {
526 self.inner.len()
527 }
528
529 /// See [`str::lines`].
530 #[inline]
531 pub fn lines(&self) -> Lines<'_> {
532 Lines {
533 inner: self.split_inclusive('\n').map(|line| {
534 let Some(line) = line.strip_suffix('\n') else {
535 return line;
536 };
537 let Some(line) = line.strip_suffix('\r') else {
538 return line;
539 };
540 line
541 }),
542 }
543 }
544
545 /// See [`str::make_ascii_lowercase`].
546 #[inline]
547 pub fn make_ascii_lowercase(&mut self) {
548 // SAFETY: changing ASCII letters only does not invalidate UTF-8.
549 let me = unsafe { self.as_bytes_mut() };
550 me.make_ascii_lowercase()
551 }
552
553 /// See [`str::make_ascii_uppercase`].
554 #[inline]
555 pub fn make_ascii_uppercase(&mut self) {
556 // SAFETY: changing ASCII letters only does not invalidate UTF-8.
557 let me = unsafe { self.as_bytes_mut() };
558 me.make_ascii_uppercase()
559 }
560
561 /// See [`str::match_indices`].
562 ///
563 /// ```
564 /// # use java_string::JavaStr;
565 /// let v: Vec<_> = JavaStr::from_str("abcXXXabcYYYabc")
566 /// .match_indices("abc")
567 /// .collect();
568 /// assert_eq!(
569 /// v,
570 /// [
571 /// (0, JavaStr::from_str("abc")),
572 /// (6, JavaStr::from_str("abc")),
573 /// (12, JavaStr::from_str("abc"))
574 /// ]
575 /// );
576 ///
577 /// let v: Vec<_> = JavaStr::from_str("1abcabc2").match_indices("abc").collect();
578 /// assert_eq!(
579 /// v,
580 /// [(1, JavaStr::from_str("abc")), (4, JavaStr::from_str("abc"))]
581 /// );
582 ///
583 /// let v: Vec<_> = JavaStr::from_str("ababa").match_indices("aba").collect();
584 /// assert_eq!(v, [(0, JavaStr::from_str("aba"))]); // only the first `aba`
585 /// ```
586 #[inline]
587 pub fn match_indices<P>(&self, pat: P) -> MatchIndices<'_, P>
588 where
589 P: JavaStrPattern,
590 {
591 MatchIndices {
592 str: self,
593 start: 0,
594 pat,
595 }
596 }
597
598 /// See [`str::matches`].
599 ///
600 /// ```
601 /// # use java_string::{JavaCodePoint, JavaStr};
602 /// let v: Vec<&JavaStr> = JavaStr::from_str("abcXXXabcYYYabc")
603 /// .matches("abc")
604 /// .collect();
605 /// assert_eq!(
606 /// v,
607 /// [
608 /// JavaStr::from_str("abc"),
609 /// JavaStr::from_str("abc"),
610 /// JavaStr::from_str("abc")
611 /// ]
612 /// );
613 ///
614 /// let v: Vec<&JavaStr> = JavaStr::from_str("1abc2abc3")
615 /// .matches(JavaCodePoint::is_numeric)
616 /// .collect();
617 /// assert_eq!(
618 /// v,
619 /// [
620 /// JavaStr::from_str("1"),
621 /// JavaStr::from_str("2"),
622 /// JavaStr::from_str("3")
623 /// ]
624 /// );
625 /// ```
626 #[inline]
627 pub fn matches<P>(&self, pat: P) -> Matches<'_, P>
628 where
629 P: JavaStrPattern,
630 {
631 Matches { str: self, pat }
632 }
633
634 /// See [`str::parse`].
635 #[inline]
636 pub fn parse<F>(&self) -> Result<F, ParseError<<F as FromStr>::Err>>
637 where
638 F: FromStr,
639 {
640 match self.as_str() {
641 Ok(str) => str.parse().map_err(ParseError::Err),
642 Err(err) => Err(ParseError::InvalidUtf8(err)),
643 }
644 }
645
646 /// See [`str::repeat`].
647 #[inline]
648 #[must_use]
649 pub fn repeat(&self, n: usize) -> JavaString {
650 unsafe { JavaString::from_semi_utf8_unchecked(self.as_bytes().repeat(n)) }
651 }
652
653 /// See [`str::replace`].
654 ///
655 /// ```
656 /// # use java_string::JavaStr;
657 /// let s = JavaStr::from_str("this is old");
658 ///
659 /// assert_eq!("this is new", s.replace("old", "new"));
660 /// assert_eq!("than an old", s.replace("is", "an"));
661 /// ```
662 #[inline]
663 #[must_use]
664 pub fn replace<P>(&self, from: P, to: &str) -> JavaString
665 where
666 P: JavaStrPattern,
667 {
668 self.replace_java(from, JavaStr::from_str(to))
669 }
670
671 /// See [`str::replace`].
672 #[inline]
673 #[must_use]
674 pub fn replace_java<P>(&self, from: P, to: &JavaStr) -> JavaString
675 where
676 P: JavaStrPattern,
677 {
678 let mut result = JavaString::new();
679 let mut last_end = 0;
680 for (start, part) in self.match_indices(from) {
681 result.push_java_str(unsafe { self.get_unchecked(last_end..start) });
682 result.push_java_str(to);
683 last_end = start + part.len();
684 }
685 result.push_java_str(unsafe { self.get_unchecked(last_end..self.len()) });
686 result
687 }
688
689 /// See [`str::replacen`].
690 ///
691 /// ```
692 /// # use java_string::{JavaCodePoint, JavaStr};
693 /// let s = JavaStr::from_str("foo foo 123 foo");
694 /// assert_eq!("new new 123 foo", s.replacen("foo", "new", 2));
695 /// assert_eq!("faa fao 123 foo", s.replacen('o', "a", 3));
696 /// assert_eq!(
697 /// "foo foo new23 foo",
698 /// s.replacen(JavaCodePoint::is_numeric, "new", 1)
699 /// );
700 /// ```
701 #[inline]
702 #[must_use]
703 pub fn replacen<P>(&self, from: P, to: &str, count: usize) -> JavaString
704 where
705 P: JavaStrPattern,
706 {
707 self.replacen_java(from, JavaStr::from_str(to), count)
708 }
709
710 /// See [`str::replacen`].
711 #[inline]
712 #[must_use]
713 pub fn replacen_java<P>(&self, from: P, to: &JavaStr, count: usize) -> JavaString
714 where
715 P: JavaStrPattern,
716 {
717 // Hope to reduce the times of re-allocation
718 let mut result = JavaString::with_capacity(32);
719 let mut last_end = 0;
720 for (start, part) in self.match_indices(from).take(count) {
721 result.push_java_str(unsafe { self.get_unchecked(last_end..start) });
722 result.push_java_str(to);
723 last_end = start + part.len();
724 }
725 result.push_java_str(unsafe { self.get_unchecked(last_end..self.len()) });
726 result
727 }
728
729 /// See [`str::rfind`].
730 ///
731 /// ```
732 /// # use java_string::JavaStr;
733 /// let s = JavaStr::from_str("Löwe 老虎 Léopard Gepardi");
734 ///
735 /// assert_eq!(s.rfind('L'), Some(13));
736 /// assert_eq!(s.rfind('é'), Some(14));
737 /// assert_eq!(s.rfind("par"), Some(24));
738 ///
739 /// let x: &[_] = &['1', '2'];
740 /// assert_eq!(s.rfind(x), None);
741 /// ```
742 #[inline]
743 #[must_use]
744 pub fn rfind<P>(&self, mut pat: P) -> Option<usize>
745 where
746 P: JavaStrPattern,
747 {
748 pat.rfind_in(self).map(|(index, _)| index)
749 }
750
751 /// See [`str::rmatch_indices`].
752 ///
753 /// ```
754 /// # use java_string::JavaStr;
755 /// let v: Vec<_> = JavaStr::from_str("abcXXXabcYYYabc")
756 /// .rmatch_indices("abc")
757 /// .collect();
758 /// assert_eq!(
759 /// v,
760 /// [
761 /// (12, JavaStr::from_str("abc")),
762 /// (6, JavaStr::from_str("abc")),
763 /// (0, JavaStr::from_str("abc"))
764 /// ]
765 /// );
766 ///
767 /// let v: Vec<_> = JavaStr::from_str("1abcabc2")
768 /// .rmatch_indices("abc")
769 /// .collect();
770 /// assert_eq!(
771 /// v,
772 /// [(4, JavaStr::from_str("abc")), (1, JavaStr::from_str("abc"))]
773 /// );
774 ///
775 /// let v: Vec<_> = JavaStr::from_str("ababa").rmatch_indices("aba").collect();
776 /// assert_eq!(v, [(2, JavaStr::from_str("aba"))]); // only the last `aba`
777 /// ```
778 #[inline]
779 pub fn rmatch_indices<P>(&self, pat: P) -> RMatchIndices<'_, P>
780 where
781 P: JavaStrPattern,
782 {
783 RMatchIndices {
784 inner: self.match_indices(pat),
785 }
786 }
787
788 /// See [`str::rmatches`].
789 ///
790 /// ```
791 /// # use java_string::{JavaCodePoint, JavaStr};
792 /// let v: Vec<&JavaStr> = JavaStr::from_str("abcXXXabcYYYabc")
793 /// .rmatches("abc")
794 /// .collect();
795 /// assert_eq!(
796 /// v,
797 /// [
798 /// JavaStr::from_str("abc"),
799 /// JavaStr::from_str("abc"),
800 /// JavaStr::from_str("abc")
801 /// ]
802 /// );
803 ///
804 /// let v: Vec<&JavaStr> = JavaStr::from_str("1abc2abc3")
805 /// .rmatches(JavaCodePoint::is_numeric)
806 /// .collect();
807 /// assert_eq!(
808 /// v,
809 /// [
810 /// JavaStr::from_str("3"),
811 /// JavaStr::from_str("2"),
812 /// JavaStr::from_str("1")
813 /// ]
814 /// );
815 /// ```
816 #[inline]
817 pub fn rmatches<P>(&self, pat: P) -> RMatches<'_, P>
818 where
819 P: JavaStrPattern,
820 {
821 RMatches {
822 inner: self.matches(pat),
823 }
824 }
825
826 /// See [`str::rsplit`].
827 ///
828 /// ```
829 /// # use java_string::JavaStr;
830 /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lamb")
831 /// .rsplit(' ')
832 /// .collect();
833 /// assert_eq!(
834 /// v,
835 /// [
836 /// JavaStr::from_str("lamb"),
837 /// JavaStr::from_str("little"),
838 /// JavaStr::from_str("a"),
839 /// JavaStr::from_str("had"),
840 /// JavaStr::from_str("Mary")
841 /// ]
842 /// );
843 ///
844 /// let v: Vec<&JavaStr> = JavaStr::from_str("").rsplit('X').collect();
845 /// assert_eq!(v, [JavaStr::from_str("")]);
846 ///
847 /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXXtigerXleopard")
848 /// .rsplit('X')
849 /// .collect();
850 /// assert_eq!(
851 /// v,
852 /// [
853 /// JavaStr::from_str("leopard"),
854 /// JavaStr::from_str("tiger"),
855 /// JavaStr::from_str(""),
856 /// JavaStr::from_str("lion")
857 /// ]
858 /// );
859 ///
860 /// let v: Vec<&JavaStr> = JavaStr::from_str("lion::tiger::leopard")
861 /// .rsplit("::")
862 /// .collect();
863 /// assert_eq!(
864 /// v,
865 /// [
866 /// JavaStr::from_str("leopard"),
867 /// JavaStr::from_str("tiger"),
868 /// JavaStr::from_str("lion")
869 /// ]
870 /// );
871 /// ```
872 #[inline]
873 pub fn rsplit<P>(&self, pat: P) -> RSplit<'_, P>
874 where
875 P: JavaStrPattern,
876 {
877 RSplit::new(self, pat)
878 }
879
880 /// See [`str::rsplit_once`].
881 ///
882 /// ```
883 /// # use java_string::JavaStr;
884 /// assert_eq!(JavaStr::from_str("cfg").rsplit_once('='), None);
885 /// assert_eq!(
886 /// JavaStr::from_str("cfg=foo").rsplit_once('='),
887 /// Some((JavaStr::from_str("cfg"), JavaStr::from_str("foo")))
888 /// );
889 /// assert_eq!(
890 /// JavaStr::from_str("cfg=foo=bar").rsplit_once('='),
891 /// Some((JavaStr::from_str("cfg=foo"), JavaStr::from_str("bar")))
892 /// );
893 /// ```
894 #[inline]
895 #[must_use]
896 pub fn rsplit_once<P>(&self, mut delimiter: P) -> Option<(&JavaStr, &JavaStr)>
897 where
898 P: JavaStrPattern,
899 {
900 let (index, len) = delimiter.rfind_in(self)?;
901 // SAFETY: pattern is known to return valid indices.
902 unsafe {
903 Some((
904 self.get_unchecked(..index),
905 self.get_unchecked(index + len..),
906 ))
907 }
908 }
909
910 /// See [`str::rsplit_terminator`].
911 ///
912 /// ```
913 /// # use java_string::JavaStr;
914 /// let v: Vec<&JavaStr> = JavaStr::from_str("A.B.").rsplit_terminator('.').collect();
915 /// assert_eq!(v, [JavaStr::from_str("B"), JavaStr::from_str("A")]);
916 ///
917 /// let v: Vec<&JavaStr> = JavaStr::from_str("A..B..").rsplit_terminator(".").collect();
918 /// assert_eq!(
919 /// v,
920 /// [
921 /// JavaStr::from_str(""),
922 /// JavaStr::from_str("B"),
923 /// JavaStr::from_str(""),
924 /// JavaStr::from_str("A")
925 /// ]
926 /// );
927 ///
928 /// let v: Vec<&JavaStr> = JavaStr::from_str("A.B:C.D")
929 /// .rsplit_terminator(&['.', ':'][..])
930 /// .collect();
931 /// assert_eq!(
932 /// v,
933 /// [
934 /// JavaStr::from_str("D"),
935 /// JavaStr::from_str("C"),
936 /// JavaStr::from_str("B"),
937 /// JavaStr::from_str("A")
938 /// ]
939 /// );
940 /// ```
941 #[inline]
942 pub fn rsplit_terminator<P>(&self, pat: P) -> RSplitTerminator<'_, P>
943 where
944 P: JavaStrPattern,
945 {
946 RSplitTerminator::new(self, pat)
947 }
948
949 /// See [`str::rsplitn`].
950 ///
951 /// ```
952 /// # use java_string::JavaStr;
953 /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lamb")
954 /// .rsplitn(3, ' ')
955 /// .collect();
956 /// assert_eq!(
957 /// v,
958 /// [
959 /// JavaStr::from_str("lamb"),
960 /// JavaStr::from_str("little"),
961 /// JavaStr::from_str("Mary had a")
962 /// ]
963 /// );
964 ///
965 /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXXtigerXleopard")
966 /// .rsplitn(3, 'X')
967 /// .collect();
968 /// assert_eq!(
969 /// v,
970 /// [
971 /// JavaStr::from_str("leopard"),
972 /// JavaStr::from_str("tiger"),
973 /// JavaStr::from_str("lionX")
974 /// ]
975 /// );
976 ///
977 /// let v: Vec<&JavaStr> = JavaStr::from_str("lion::tiger::leopard")
978 /// .rsplitn(2, "::")
979 /// .collect();
980 /// assert_eq!(
981 /// v,
982 /// [
983 /// JavaStr::from_str("leopard"),
984 /// JavaStr::from_str("lion::tiger")
985 /// ]
986 /// );
987 /// ```
988 #[inline]
989 pub fn rsplitn<P>(&self, n: usize, pat: P) -> RSplitN<'_, P>
990 where
991 P: JavaStrPattern,
992 {
993 RSplitN::new(self, pat, n)
994 }
995
996 /// See [`str::split`].
997 ///
998 /// ```
999 /// # use java_string::{JavaCodePoint, JavaStr};
1000 /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lamb")
1001 /// .split(' ')
1002 /// .collect();
1003 /// assert_eq!(
1004 /// v,
1005 /// [
1006 /// JavaStr::from_str("Mary"),
1007 /// JavaStr::from_str("had"),
1008 /// JavaStr::from_str("a"),
1009 /// JavaStr::from_str("little"),
1010 /// JavaStr::from_str("lamb")
1011 /// ]
1012 /// );
1013 ///
1014 /// let v: Vec<&JavaStr> = JavaStr::from_str("").split('X').collect();
1015 /// assert_eq!(v, [JavaStr::from_str("")]);
1016 ///
1017 /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXXtigerXleopard")
1018 /// .split('X')
1019 /// .collect();
1020 /// assert_eq!(
1021 /// v,
1022 /// [
1023 /// JavaStr::from_str("lion"),
1024 /// JavaStr::from_str(""),
1025 /// JavaStr::from_str("tiger"),
1026 /// JavaStr::from_str("leopard")
1027 /// ]
1028 /// );
1029 ///
1030 /// let v: Vec<&JavaStr> = JavaStr::from_str("lion::tiger::leopard")
1031 /// .split("::")
1032 /// .collect();
1033 /// assert_eq!(
1034 /// v,
1035 /// [
1036 /// JavaStr::from_str("lion"),
1037 /// JavaStr::from_str("tiger"),
1038 /// JavaStr::from_str("leopard")
1039 /// ]
1040 /// );
1041 ///
1042 /// let v: Vec<&JavaStr> = JavaStr::from_str("abc1def2ghi")
1043 /// .split(JavaCodePoint::is_numeric)
1044 /// .collect();
1045 /// assert_eq!(
1046 /// v,
1047 /// [
1048 /// JavaStr::from_str("abc"),
1049 /// JavaStr::from_str("def"),
1050 /// JavaStr::from_str("ghi")
1051 /// ]
1052 /// );
1053 ///
1054 /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXtigerXleopard")
1055 /// .split(JavaCodePoint::is_uppercase)
1056 /// .collect();
1057 /// assert_eq!(
1058 /// v,
1059 /// [
1060 /// JavaStr::from_str("lion"),
1061 /// JavaStr::from_str("tiger"),
1062 /// JavaStr::from_str("leopard")
1063 /// ]
1064 /// );
1065 /// ```
1066 #[inline]
1067 pub fn split<P>(&self, pat: P) -> Split<'_, P>
1068 where
1069 P: JavaStrPattern,
1070 {
1071 Split::new(self, pat)
1072 }
1073
1074 /// See [`str::split_ascii_whitespace`].
1075 ///
1076 /// ```
1077 /// # use java_string::JavaStr;
1078 /// let mut iter = JavaStr::from_str(" Mary had\ta little \n\t lamb").split_ascii_whitespace();
1079 /// assert_eq!(Some(JavaStr::from_str("Mary")), iter.next());
1080 /// assert_eq!(Some(JavaStr::from_str("had")), iter.next());
1081 /// assert_eq!(Some(JavaStr::from_str("a")), iter.next());
1082 /// assert_eq!(Some(JavaStr::from_str("little")), iter.next());
1083 /// assert_eq!(Some(JavaStr::from_str("lamb")), iter.next());
1084 ///
1085 /// assert_eq!(None, iter.next());
1086 /// ```
1087 #[inline]
1088 pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> {
1089 #[inline]
1090 fn is_non_empty(bytes: &&[u8]) -> bool {
1091 !bytes.is_empty()
1092 }
1093
1094 SplitAsciiWhitespace {
1095 inner: self
1096 .as_bytes()
1097 .split(u8::is_ascii_whitespace as fn(&u8) -> bool)
1098 .filter(is_non_empty as fn(&&[u8]) -> bool)
1099 .map(|bytes| unsafe { JavaStr::from_semi_utf8_unchecked(bytes) }),
1100 }
1101 }
1102
1103 /// See [`str::split_at`].
1104 ///
1105 /// ```
1106 /// # use java_string::JavaStr;
1107 /// let s = JavaStr::from_str("Per Martin-Löf");
1108 ///
1109 /// let (first, last) = s.split_at(3);
1110 ///
1111 /// assert_eq!("Per", first);
1112 /// assert_eq!(" Martin-Löf", last);
1113 /// ```
1114 /// ```should_panic
1115 /// # use java_string::JavaStr;
1116 /// let s = JavaStr::from_str("Per Martin-Löf");
1117 /// // Should panic
1118 /// let _ = s.split_at(13);
1119 /// ```
1120 #[inline]
1121 #[must_use]
1122 pub fn split_at(&self, mid: usize) -> (&JavaStr, &JavaStr) {
1123 // is_char_boundary checks that the index is in [0, .len()]
1124 if self.is_char_boundary(mid) {
1125 // SAFETY: just checked that `mid` is on a char boundary.
1126 unsafe {
1127 (
1128 self.get_unchecked(0..mid),
1129 self.get_unchecked(mid..self.len()),
1130 )
1131 }
1132 } else {
1133 slice_error_fail(self, 0, mid)
1134 }
1135 }
1136
1137 /// See [`str::split_at_mut`].
1138 ///
1139 /// ```
1140 /// # use java_string::{JavaStr, JavaString};
1141 /// let mut s = JavaString::from("Per Martin-Löf");
1142 /// let s = s.as_mut_java_str();
1143 ///
1144 /// let (first, last) = s.split_at_mut(3);
1145 ///
1146 /// assert_eq!("Per", first);
1147 /// assert_eq!(" Martin-Löf", last);
1148 /// ```
1149 /// ```should_panic
1150 /// # use java_string::{JavaStr, JavaString};
1151 /// let mut s = JavaString::from("Per Martin-Löf");
1152 /// let s = s.as_mut_java_str();
1153 /// // Should panic
1154 /// let _ = s.split_at(13);
1155 /// ```
1156 #[inline]
1157 #[must_use]
1158 pub fn split_at_mut(&mut self, mid: usize) -> (&mut JavaStr, &mut JavaStr) {
1159 // is_char_boundary checks that the index is in [0, .len()]
1160 if self.is_char_boundary(mid) {
1161 // SAFETY: just checked that `mid` is on a char boundary.
1162 unsafe { self.split_at_mut_unchecked(mid) }
1163 } else {
1164 slice_error_fail(self, 0, mid)
1165 }
1166 }
1167
1168 /// See [`str::split_at_checked`].
1169 ///
1170 /// ```
1171 /// # use java_string::JavaStr;
1172 /// let s = JavaStr::from_str("Per Martin-Löf");
1173 ///
1174 /// let (first, last) = s.split_at_checked(3).unwrap();
1175 /// assert_eq!("Per", first);
1176 /// assert_eq!(" Martin-Löf", last);
1177 ///
1178 /// assert_eq!(None, s.split_at_checked(13)); // Inside “ö”
1179 /// assert_eq!(None, s.split_at_checked(16)); // Beyond the string length
1180 /// ```
1181 #[inline]
1182 #[must_use]
1183 pub const fn split_at_checked(&self, mid: usize) -> Option<(&JavaStr, &JavaStr)> {
1184 // is_char_boundary checks that the index is in [0, .len()]
1185 if self.is_char_boundary(mid) {
1186 // SAFETY: just checked that `mid` is on a char boundary.
1187 unsafe { Some(self.split_at_unchecked(mid)) }
1188 } else {
1189 None
1190 }
1191 }
1192
1193 /// # Safety
1194 ///
1195 /// Caller must ensure that `mid` lies on a valid char boundary
1196 #[inline]
1197 const unsafe fn split_at_unchecked(&self, mid: usize) -> (&JavaStr, &JavaStr) {
1198 let len = self.len();
1199 let ptr = self.as_ptr();
1200 // SAFETY: caller guarantees `mid` is on a char boundary.
1201 unsafe {
1202 (
1203 Self::from_semi_utf8_unchecked(slice::from_raw_parts(ptr, mid)),
1204 Self::from_semi_utf8_unchecked(slice::from_raw_parts(ptr.add(mid), len - mid)),
1205 )
1206 }
1207 }
1208
1209 /// See [`str::split_at_mut_checked`].
1210 ///
1211 /// ```
1212 /// # use java_string::{JavaStr, JavaString};
1213 /// let mut s = JavaString::from("Per Martin-Löf");
1214 /// let mut s = s.as_mut_java_str();
1215 /// if let Some((first, last)) = s.split_at_mut_checked(3) {
1216 /// first.make_ascii_uppercase();
1217 /// assert_eq!("PER", first);
1218 /// assert_eq!(" Martin-Löf", last);
1219 /// }
1220 /// assert_eq!("PER Martin-Löf", s);
1221 ///
1222 /// assert_eq!(None, s.split_at_mut_checked(13)); // Inside “ö”
1223 /// assert_eq!(None, s.split_at_mut_checked(16)); // Beyond the string length
1224 /// ```
1225 #[inline]
1226 #[must_use]
1227 pub const fn split_at_mut_checked(
1228 &mut self,
1229 mid: usize,
1230 ) -> Option<(&mut JavaStr, &mut JavaStr)> {
1231 // is_char_boundary checks that the index is in [0, .len()]
1232 if self.is_char_boundary(mid) {
1233 // SAFETY: just checked that `mid` is on a char boundary.
1234 unsafe { Some(self.split_at_mut_unchecked(mid)) }
1235 } else {
1236 None
1237 }
1238 }
1239
1240 /// # Safety
1241 ///
1242 /// Caller must ensure that `mid` lies on a valid char boundary
1243 #[inline]
1244 const unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut JavaStr, &mut JavaStr) {
1245 let len = self.len();
1246 let ptr = self.as_mut_ptr();
1247 // SAFETY: caller guarantees `mid` is on a char boundary.
1248 unsafe {
1249 (
1250 Self::from_semi_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)),
1251 Self::from_semi_utf8_unchecked_mut(slice::from_raw_parts_mut(
1252 ptr.add(mid),
1253 len - mid,
1254 )),
1255 )
1256 }
1257 }
1258
1259 /// See [`str::split_inclusive`].
1260 ///
1261 /// ```
1262 /// # use java_string::JavaStr;
1263 /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lamb\nlittle lamb\nlittle lamb.\n")
1264 /// .split_inclusive('\n')
1265 /// .collect();
1266 /// assert_eq!(
1267 /// v,
1268 /// [
1269 /// JavaStr::from_str("Mary had a little lamb\n"),
1270 /// JavaStr::from_str("little lamb\n"),
1271 /// JavaStr::from_str("little lamb.\n")
1272 /// ]
1273 /// );
1274 /// ```
1275 #[inline]
1276 pub fn split_inclusive<P>(&self, pat: P) -> SplitInclusive<'_, P>
1277 where
1278 P: JavaStrPattern,
1279 {
1280 SplitInclusive::new(self, pat)
1281 }
1282
1283 /// See [`str::split_once`].
1284 ///
1285 /// ```
1286 /// # use java_string::JavaStr;
1287 /// assert_eq!(JavaStr::from_str("cfg").split_once('='), None);
1288 /// assert_eq!(
1289 /// JavaStr::from_str("cfg=").split_once('='),
1290 /// Some((JavaStr::from_str("cfg"), JavaStr::from_str("")))
1291 /// );
1292 /// assert_eq!(
1293 /// JavaStr::from_str("cfg=foo").split_once('='),
1294 /// Some((JavaStr::from_str("cfg"), JavaStr::from_str("foo")))
1295 /// );
1296 /// assert_eq!(
1297 /// JavaStr::from_str("cfg=foo=bar").split_once('='),
1298 /// Some((JavaStr::from_str("cfg"), JavaStr::from_str("foo=bar")))
1299 /// );
1300 /// ```
1301 #[inline]
1302 #[must_use]
1303 pub fn split_once<P>(&self, mut delimiter: P) -> Option<(&JavaStr, &JavaStr)>
1304 where
1305 P: JavaStrPattern,
1306 {
1307 let (index, len) = delimiter.find_in(self)?;
1308 // SAFETY: pattern is known to return valid indices.
1309 unsafe {
1310 Some((
1311 self.get_unchecked(..index),
1312 self.get_unchecked(index + len..),
1313 ))
1314 }
1315 }
1316
1317 /// See [`str::split_terminator`].
1318 ///
1319 /// ```
1320 /// # use java_string::JavaStr;
1321 /// let v: Vec<&JavaStr> = JavaStr::from_str("A.B.").split_terminator('.').collect();
1322 /// assert_eq!(v, [JavaStr::from_str("A"), JavaStr::from_str("B")]);
1323 ///
1324 /// let v: Vec<&JavaStr> = JavaStr::from_str("A..B..").split_terminator(".").collect();
1325 /// assert_eq!(
1326 /// v,
1327 /// [
1328 /// JavaStr::from_str("A"),
1329 /// JavaStr::from_str(""),
1330 /// JavaStr::from_str("B"),
1331 /// JavaStr::from_str("")
1332 /// ]
1333 /// );
1334 ///
1335 /// let v: Vec<&JavaStr> = JavaStr::from_str("A.B:C.D")
1336 /// .split_terminator(&['.', ':'][..])
1337 /// .collect();
1338 /// assert_eq!(
1339 /// v,
1340 /// [
1341 /// JavaStr::from_str("A"),
1342 /// JavaStr::from_str("B"),
1343 /// JavaStr::from_str("C"),
1344 /// JavaStr::from_str("D")
1345 /// ]
1346 /// );
1347 /// ```
1348 #[inline]
1349 pub fn split_terminator<P>(&self, pat: P) -> SplitTerminator<'_, P>
1350 where
1351 P: JavaStrPattern,
1352 {
1353 SplitTerminator::new(self, pat)
1354 }
1355
1356 /// See [`str::split_whitespace`].
1357 #[inline]
1358 pub fn split_whitespace(&self) -> SplitWhitespace<'_> {
1359 SplitWhitespace {
1360 inner: self
1361 .split(JavaCodePoint::is_whitespace as fn(JavaCodePoint) -> bool)
1362 .filter(|str| !str.is_empty()),
1363 }
1364 }
1365
1366 /// See [`str::splitn`].
1367 ///
1368 /// ```
1369 /// # use java_string::JavaStr;
1370 /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lambda")
1371 /// .splitn(3, ' ')
1372 /// .collect();
1373 /// assert_eq!(
1374 /// v,
1375 /// [
1376 /// JavaStr::from_str("Mary"),
1377 /// JavaStr::from_str("had"),
1378 /// JavaStr::from_str("a little lambda")
1379 /// ]
1380 /// );
1381 ///
1382 /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXXtigerXleopard")
1383 /// .splitn(3, "X")
1384 /// .collect();
1385 /// assert_eq!(
1386 /// v,
1387 /// [
1388 /// JavaStr::from_str("lion"),
1389 /// JavaStr::from_str(""),
1390 /// JavaStr::from_str("tigerXleopard")
1391 /// ]
1392 /// );
1393 ///
1394 /// let v: Vec<&JavaStr> = JavaStr::from_str("abcXdef").splitn(1, 'X').collect();
1395 /// assert_eq!(v, [JavaStr::from_str("abcXdef")]);
1396 ///
1397 /// let v: Vec<&JavaStr> = JavaStr::from_str("").splitn(1, 'X').collect();
1398 /// assert_eq!(v, [JavaStr::from_str("")]);
1399 /// ```
1400 #[inline]
1401 pub fn splitn<P>(&self, n: usize, pat: P) -> SplitN<'_, P>
1402 where
1403 P: JavaStrPattern,
1404 {
1405 SplitN::new(self, pat, n)
1406 }
1407
1408 /// See [`str::starts_with`].
1409 ///
1410 /// ```
1411 /// # use java_string::JavaStr;
1412 /// let bananas = JavaStr::from_str("bananas");
1413 ///
1414 /// assert!(bananas.starts_with("bana"));
1415 /// assert!(!bananas.starts_with("nana"));
1416 /// ```
1417 #[inline]
1418 #[must_use]
1419 pub fn starts_with<P>(&self, mut pat: P) -> bool
1420 where
1421 P: JavaStrPattern,
1422 {
1423 pat.prefix_len_in(self).is_some()
1424 }
1425
1426 /// See [`str::strip_prefix`].
1427 ///
1428 /// ```
1429 /// # use java_string::JavaStr;
1430 /// assert_eq!(
1431 /// JavaStr::from_str("foo:bar").strip_prefix("foo:"),
1432 /// Some(JavaStr::from_str("bar"))
1433 /// );
1434 /// assert_eq!(JavaStr::from_str("foo:bar").strip_prefix("bar"), None);
1435 /// assert_eq!(
1436 /// JavaStr::from_str("foofoo").strip_prefix("foo"),
1437 /// Some(JavaStr::from_str("foo"))
1438 /// );
1439 /// ```
1440 #[inline]
1441 #[must_use]
1442 pub fn strip_prefix<P>(&self, mut prefix: P) -> Option<&JavaStr>
1443 where
1444 P: JavaStrPattern,
1445 {
1446 let len = prefix.prefix_len_in(self)?;
1447 // SAFETY: pattern is known to return valid indices.
1448 unsafe { Some(self.get_unchecked(len..)) }
1449 }
1450
1451 /// See [`str::strip_suffix`].
1452 ///
1453 /// ```
1454 /// # use java_string::JavaStr;
1455 /// assert_eq!(
1456 /// JavaStr::from_str("bar:foo").strip_suffix(":foo"),
1457 /// Some(JavaStr::from_str("bar"))
1458 /// );
1459 /// assert_eq!(JavaStr::from_str("bar:foo").strip_suffix("bar"), None);
1460 /// assert_eq!(
1461 /// JavaStr::from_str("foofoo").strip_suffix("foo"),
1462 /// Some(JavaStr::from_str("foo"))
1463 /// );
1464 /// ```
1465 #[inline]
1466 #[must_use]
1467 pub fn strip_suffix<P>(&self, mut suffix: P) -> Option<&JavaStr>
1468 where
1469 P: JavaStrPattern,
1470 {
1471 let len = suffix.suffix_len_in(self)?;
1472 // SAFETY: pattern is known to return valid indices.
1473 unsafe { Some(self.get_unchecked(..self.len() - len)) }
1474 }
1475
1476 /// See [`str::to_ascii_lowercase`].
1477 #[inline]
1478 #[must_use]
1479 pub fn to_ascii_lowercase(&self) -> JavaString {
1480 let mut s = self.to_owned();
1481 s.make_ascii_lowercase();
1482 s
1483 }
1484
1485 /// See [`str::to_ascii_uppercase`].
1486 #[inline]
1487 #[must_use]
1488 pub fn to_ascii_uppercase(&self) -> JavaString {
1489 let mut s = self.to_owned();
1490 s.make_ascii_uppercase();
1491 s
1492 }
1493
1494 /// See [`str::to_lowercase`].
1495 ///
1496 /// ```
1497 /// # use java_string::{JavaCodePoint, JavaStr, JavaString};
1498 /// let s = JavaStr::from_str("HELLO");
1499 /// assert_eq!("hello", s.to_lowercase());
1500 ///
1501 /// let odysseus = JavaStr::from_str("ὈΔΥΣΣΕΎΣ");
1502 /// assert_eq!("ὀδυσσεύς", odysseus.to_lowercase());
1503 ///
1504 /// let s = JavaString::from("Hello ")
1505 /// + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1506 /// + JavaStr::from_str(" World!");
1507 /// let expected = JavaString::from("hello ")
1508 /// + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1509 /// + JavaStr::from_str(" world!");
1510 /// assert_eq!(expected, s.to_lowercase());
1511 /// ```
1512 #[inline]
1513 #[must_use]
1514 pub fn to_lowercase(&self) -> JavaString {
1515 self.transform_string(str::to_lowercase, |ch| ch)
1516 }
1517
1518 /// See [`str::to_uppercase`].
1519 ///
1520 /// ```
1521 /// # use java_string::{JavaCodePoint, JavaStr, JavaString};
1522 /// let s = JavaStr::from_str("hello");
1523 /// assert_eq!("HELLO", s.to_uppercase());
1524 ///
1525 /// let s = JavaStr::from_str("tschüß");
1526 /// assert_eq!("TSCHÜSS", s.to_uppercase());
1527 ///
1528 /// let s = JavaString::from("Hello ")
1529 /// + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1530 /// + JavaStr::from_str(" World!");
1531 /// let expected = JavaString::from("HELLO ")
1532 /// + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1533 /// + JavaStr::from_str(" WORLD!");
1534 /// assert_eq!(expected, s.to_uppercase());
1535 /// ```
1536 #[inline]
1537 #[must_use]
1538 pub fn to_uppercase(&self) -> JavaString {
1539 self.transform_string(str::to_uppercase, |ch| ch)
1540 }
1541
1542 /// See [`str::trim`].
1543 #[inline]
1544 #[must_use]
1545 pub fn trim(&self) -> &JavaStr {
1546 self.trim_matches(|c: JavaCodePoint| c.is_whitespace())
1547 }
1548
1549 /// See [`str::trim_ascii`]
1550 #[inline]
1551 #[must_use]
1552 pub fn trim_ascii(&self) -> &JavaStr {
1553 self.trim_matches(|c: JavaCodePoint| c.is_ascii_whitespace())
1554 }
1555
1556 /// See [`str::trim_end`].
1557 #[inline]
1558 #[must_use]
1559 pub fn trim_end(&self) -> &JavaStr {
1560 self.trim_end_matches(|c: JavaCodePoint| c.is_whitespace())
1561 }
1562
1563 /// See [`str::trim_ascii_end`]
1564 pub fn trim_ascii_end(&self) -> &JavaStr {
1565 self.trim_end_matches(|c: JavaCodePoint| c.is_ascii_whitespace())
1566 }
1567
1568 /// See [`str::trim_end_matches`].
1569 ///
1570 /// ```
1571 /// # use java_string::{JavaCodePoint, JavaStr};
1572 /// assert_eq!(
1573 /// JavaStr::from_str("11foo1bar11").trim_end_matches('1'),
1574 /// "11foo1bar"
1575 /// );
1576 /// assert_eq!(
1577 /// JavaStr::from_str("123foo1bar123").trim_end_matches(JavaCodePoint::is_numeric),
1578 /// "123foo1bar"
1579 /// );
1580 ///
1581 /// let x: &[_] = &['1', '2'];
1582 /// assert_eq!(
1583 /// JavaStr::from_str("12foo1bar12").trim_end_matches(x),
1584 /// "12foo1bar"
1585 /// );
1586 /// ```
1587 #[inline]
1588 #[must_use]
1589 pub fn trim_end_matches<P>(&self, mut pat: P) -> &JavaStr
1590 where
1591 P: JavaStrPattern,
1592 {
1593 let mut str = self;
1594 while let Some(suffix_len) = pat.suffix_len_in(str) {
1595 if suffix_len == 0 {
1596 break;
1597 }
1598 // SAFETY: pattern is known to return valid indices.
1599 str = unsafe { str.get_unchecked(..str.len() - suffix_len) };
1600 }
1601 str
1602 }
1603
1604 /// See [`str::trim_matches`].
1605 ///
1606 /// ```
1607 /// # use java_string::{JavaCodePoint, JavaStr};
1608 /// assert_eq!(
1609 /// JavaStr::from_str("11foo1bar11").trim_matches('1'),
1610 /// "foo1bar"
1611 /// );
1612 /// assert_eq!(
1613 /// JavaStr::from_str("123foo1bar123").trim_matches(JavaCodePoint::is_numeric),
1614 /// "foo1bar"
1615 /// );
1616 ///
1617 /// let x: &[_] = &['1', '2'];
1618 /// assert_eq!(JavaStr::from_str("12foo1bar12").trim_matches(x), "foo1bar");
1619 /// ```
1620 #[inline]
1621 #[must_use]
1622 pub fn trim_matches<P>(&self, mut pat: P) -> &JavaStr
1623 where
1624 P: JavaStrPattern,
1625 {
1626 let mut str = self;
1627 while let Some(prefix_len) = pat.prefix_len_in(str) {
1628 if prefix_len == 0 {
1629 break;
1630 }
1631 // SAFETY: pattern is known to return valid indices.
1632 str = unsafe { str.get_unchecked(prefix_len..) };
1633 }
1634 while let Some(suffix_len) = pat.suffix_len_in(str) {
1635 if suffix_len == 0 {
1636 break;
1637 }
1638 // SAFETY: pattern is known to return valid indices.
1639 str = unsafe { str.get_unchecked(..str.len() - suffix_len) };
1640 }
1641 str
1642 }
1643
1644 /// See [`str::trim_start`].
1645 #[inline]
1646 #[must_use]
1647 pub fn trim_start(&self) -> &JavaStr {
1648 self.trim_start_matches(|c: JavaCodePoint| c.is_whitespace())
1649 }
1650
1651 /// See [`str::trim_ascii_start`]
1652 #[inline]
1653 #[must_use]
1654 pub fn trim_ascii_start(&self) -> &JavaStr {
1655 self.trim_start_matches(|c: JavaCodePoint| c.is_ascii_whitespace())
1656 }
1657
1658 /// See [`str::trim_start_matches`].
1659 ///
1660 /// ```
1661 /// # use java_string::{JavaCodePoint, JavaStr};
1662 /// assert_eq!(
1663 /// JavaStr::from_str("11foo1bar11").trim_start_matches('1'),
1664 /// "foo1bar11"
1665 /// );
1666 /// assert_eq!(
1667 /// JavaStr::from_str("123foo1bar123").trim_start_matches(JavaCodePoint::is_numeric),
1668 /// "foo1bar123"
1669 /// );
1670 ///
1671 /// let x: &[_] = &['1', '2'];
1672 /// assert_eq!(
1673 /// JavaStr::from_str("12foo1bar12").trim_start_matches(x),
1674 /// "foo1bar12"
1675 /// );
1676 /// ```
1677 #[inline]
1678 #[must_use]
1679 pub fn trim_start_matches<P>(&self, mut pat: P) -> &JavaStr
1680 where
1681 P: JavaStrPattern,
1682 {
1683 let mut str = self;
1684 while let Some(prefix_len) = pat.prefix_len_in(str) {
1685 if prefix_len == 0 {
1686 break;
1687 }
1688 // SAFETY: pattern is known to return valid indices.
1689 str = unsafe { str.get_unchecked(prefix_len..) };
1690 }
1691 str
1692 }
1693
1694 #[inline]
1695 fn transform_string<SF, ICF>(
1696 &self,
1697 mut string_transformer: SF,
1698 invalid_char_transformer: ICF,
1699 ) -> JavaString
1700 where
1701 SF: FnMut(&str) -> String,
1702 ICF: FnMut(&JavaStr) -> &JavaStr,
1703 {
1704 let bytes = self.as_bytes();
1705 match run_utf8_full_validation_from_semi(bytes) {
1706 Ok(()) => JavaString::from(string_transformer(unsafe {
1707 // SAFETY: validation succeeded
1708 std::str::from_utf8_unchecked(bytes)
1709 })),
1710 Err(error) => {
1711 self.transform_invalid_string(error, string_transformer, invalid_char_transformer)
1712 }
1713 }
1714 }
1715
1716 #[inline]
1717 fn transform_invalid_string<SF, ICF>(
1718 &self,
1719 error: Utf8Error,
1720 mut string_transformer: SF,
1721 mut invalid_char_transformer: ICF,
1722 ) -> JavaString
1723 where
1724 SF: FnMut(&str) -> String,
1725 ICF: FnMut(&JavaStr) -> &JavaStr,
1726 {
1727 let bytes = self.as_bytes();
1728 let mut result = JavaString::from(string_transformer(unsafe {
1729 // SAFETY: validation succeeded up to this index
1730 std::str::from_utf8_unchecked(bytes.get_unchecked(..error.valid_up_to))
1731 }));
1732 result.push_java_str(invalid_char_transformer(unsafe {
1733 // SAFETY: any UTF-8 error in semi-valid UTF-8 is a 3 byte long sequence
1734 // representing a surrogate code point. We're pushing that sequence now
1735 JavaStr::from_semi_utf8_unchecked(
1736 bytes.get_unchecked(error.valid_up_to..error.valid_up_to + 3),
1737 )
1738 }));
1739 let mut index = error.valid_up_to + 3;
1740 loop {
1741 let remainder = unsafe { bytes.get_unchecked(index..) };
1742 match run_utf8_full_validation_from_semi(remainder) {
1743 Ok(()) => {
1744 result.push_str(&string_transformer(unsafe {
1745 // SAFETY: validation succeeded
1746 std::str::from_utf8_unchecked(remainder)
1747 }));
1748 return result;
1749 }
1750 Err(error) => {
1751 result.push_str(&string_transformer(unsafe {
1752 // SAFETY: validation succeeded up to this index
1753 std::str::from_utf8_unchecked(
1754 bytes.get_unchecked(index..index + error.valid_up_to),
1755 )
1756 }));
1757 result.push_java_str(invalid_char_transformer(unsafe {
1758 // SAFETY: see comment above
1759 JavaStr::from_semi_utf8_unchecked(bytes.get_unchecked(
1760 index + error.valid_up_to..index + error.valid_up_to + 3,
1761 ))
1762 }));
1763 index += error.valid_up_to + 3;
1764 }
1765 }
1766 }
1767 }
1768}
1769
1770impl<'a> Add<&JavaStr> for Cow<'a, JavaStr> {
1771 type Output = Cow<'a, JavaStr>;
1772
1773 #[inline]
1774 fn add(mut self, rhs: &JavaStr) -> Self::Output {
1775 self += rhs;
1776 self
1777 }
1778}
1779
1780impl AddAssign<&JavaStr> for Cow<'_, JavaStr> {
1781 #[inline]
1782 fn add_assign(&mut self, rhs: &JavaStr) {
1783 if !rhs.is_empty() {
1784 match self {
1785 Cow::Borrowed(lhs) => {
1786 let mut result = lhs.to_owned();
1787 result.push_java_str(rhs);
1788 *self = Cow::Owned(result);
1789 }
1790 Cow::Owned(lhs) => {
1791 lhs.push_java_str(rhs);
1792 }
1793 }
1794 }
1795 }
1796}
1797
1798impl AsRef<[u8]> for JavaStr {
1799 #[inline]
1800 fn as_ref(&self) -> &[u8] {
1801 self.as_bytes()
1802 }
1803}
1804
1805impl AsRef<JavaStr> for str {
1806 #[inline]
1807 fn as_ref(&self) -> &JavaStr {
1808 JavaStr::from_str(self)
1809 }
1810}
1811
1812impl AsRef<JavaStr> for String {
1813 #[inline]
1814 fn as_ref(&self) -> &JavaStr {
1815 JavaStr::from_str(self)
1816 }
1817}
1818
1819impl AsRef<JavaStr> for JavaStr {
1820 #[inline]
1821 fn as_ref(&self) -> &JavaStr {
1822 self
1823 }
1824}
1825
1826impl Clone for Box<JavaStr> {
1827 #[inline]
1828 fn clone(&self) -> Self {
1829 let buf: Box<[u8]> = self.as_bytes().into();
1830 unsafe { JavaStr::from_boxed_semi_utf8_unchecked(buf) }
1831 }
1832}
1833
1834impl Debug for JavaStr {
1835 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1836 f.write_char('"')?;
1837 let mut from = 0;
1838 for (i, c) in self.char_indices() {
1839 let esc = c.escape_debug_ext(EscapeDebugExtArgs {
1840 escape_single_quote: false,
1841 escape_double_quote: true,
1842 });
1843 // If char needs escaping, flush backlog so far and write, else skip.
1844 // Also handle invalid UTF-8 here
1845 if esc.len() != 1 || c.as_char().is_none() {
1846 unsafe {
1847 // SAFETY: any invalid UTF-8 should have been caught by a previous iteration
1848 f.write_str(self[from..i].as_str_unchecked())?
1849 };
1850 for c in esc {
1851 f.write_char(c)?;
1852 }
1853 from = i + c.len_utf8();
1854 }
1855 }
1856 unsafe {
1857 // SAFETY: any invalid UTF-8 should have been caught by the loop above
1858 f.write_str(self[from..].as_str_unchecked())?
1859 };
1860 f.write_char('"')
1861 }
1862}
1863
1864impl Default for &JavaStr {
1865 #[inline]
1866 fn default() -> Self {
1867 JavaStr::from_str("")
1868 }
1869}
1870
1871impl Default for Box<JavaStr> {
1872 #[inline]
1873 fn default() -> Self {
1874 JavaStr::from_boxed_str(Box::<str>::default())
1875 }
1876}
1877
1878impl Display for JavaStr {
1879 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1880 Display::fmt(&self.as_str_lossy(), f)
1881 }
1882}
1883
1884impl<'a> From<&'a JavaStr> for Cow<'a, JavaStr> {
1885 #[inline]
1886 fn from(value: &'a JavaStr) -> Self {
1887 Cow::Borrowed(value)
1888 }
1889}
1890
1891impl From<&JavaStr> for Arc<JavaStr> {
1892 #[inline]
1893 fn from(value: &JavaStr) -> Self {
1894 let arc = Arc::<[u8]>::from(value.as_bytes());
1895 unsafe { Arc::from_raw(Arc::into_raw(arc) as *const JavaStr) }
1896 }
1897}
1898
1899impl From<&JavaStr> for Box<JavaStr> {
1900 #[inline]
1901 fn from(value: &JavaStr) -> Self {
1902 unsafe { JavaStr::from_boxed_semi_utf8_unchecked(Box::from(value.as_bytes())) }
1903 }
1904}
1905
1906impl From<&JavaStr> for Rc<JavaStr> {
1907 #[inline]
1908 fn from(value: &JavaStr) -> Self {
1909 let rc = Rc::<[u8]>::from(value.as_bytes());
1910 unsafe { Rc::from_raw(Rc::into_raw(rc) as *const JavaStr) }
1911 }
1912}
1913
1914impl From<&JavaStr> for Vec<u8> {
1915 #[inline]
1916 fn from(value: &JavaStr) -> Self {
1917 From::from(value.as_bytes())
1918 }
1919}
1920
1921impl From<Cow<'_, JavaStr>> for Box<JavaStr> {
1922 #[inline]
1923 fn from(value: Cow<'_, JavaStr>) -> Self {
1924 match value {
1925 Cow::Borrowed(s) => Box::from(s),
1926 Cow::Owned(s) => Box::from(s),
1927 }
1928 }
1929}
1930
1931impl From<JavaString> for Box<JavaStr> {
1932 #[inline]
1933 fn from(value: JavaString) -> Self {
1934 value.into_boxed_str()
1935 }
1936}
1937
1938impl<'a> From<&'a str> for &'a JavaStr {
1939 #[inline]
1940 fn from(value: &'a str) -> Self {
1941 JavaStr::from_str(value)
1942 }
1943}
1944
1945impl<'a> From<&'a String> for &'a JavaStr {
1946 #[inline]
1947 fn from(value: &'a String) -> Self {
1948 JavaStr::from_str(value)
1949 }
1950}
1951
1952impl FromIterator<char> for Box<JavaStr> {
1953 #[inline]
1954 fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
1955 JavaString::from_iter(iter).into_boxed_str()
1956 }
1957}
1958
1959impl<'a> FromIterator<&'a char> for Box<JavaStr> {
1960 #[inline]
1961 fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
1962 JavaString::from_iter(iter).into_boxed_str()
1963 }
1964}
1965
1966impl FromIterator<JavaCodePoint> for Box<JavaStr> {
1967 #[inline]
1968 fn from_iter<T: IntoIterator<Item = JavaCodePoint>>(iter: T) -> Self {
1969 JavaString::from_iter(iter).into_boxed_str()
1970 }
1971}
1972
1973impl<'a> FromIterator<&'a JavaCodePoint> for Box<JavaStr> {
1974 #[inline]
1975 fn from_iter<T: IntoIterator<Item = &'a JavaCodePoint>>(iter: T) -> Self {
1976 JavaString::from_iter(iter).into_boxed_str()
1977 }
1978}
1979
1980impl<'a> FromIterator<&'a str> for Box<JavaStr> {
1981 #[inline]
1982 fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
1983 JavaString::from_iter(iter).into_boxed_str()
1984 }
1985}
1986
1987impl<'a> FromIterator<&'a JavaStr> for Box<JavaStr> {
1988 #[inline]
1989 fn from_iter<T: IntoIterator<Item = &'a JavaStr>>(iter: T) -> Self {
1990 JavaString::from_iter(iter).into_boxed_str()
1991 }
1992}
1993
1994impl FromIterator<String> for Box<JavaStr> {
1995 fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
1996 JavaString::from_iter(iter).into_boxed_str()
1997 }
1998}
1999
2000impl FromIterator<JavaString> for Box<JavaStr> {
2001 fn from_iter<T: IntoIterator<Item = JavaString>>(iter: T) -> Self {
2002 JavaString::from_iter(iter).into_boxed_str()
2003 }
2004}
2005
2006impl FromIterator<Box<str>> for Box<JavaStr> {
2007 #[inline]
2008 fn from_iter<T: IntoIterator<Item = Box<str>>>(iter: T) -> Self {
2009 JavaString::from_iter(iter).into_boxed_str()
2010 }
2011}
2012
2013impl FromIterator<Box<JavaStr>> for Box<JavaStr> {
2014 #[inline]
2015 fn from_iter<T: IntoIterator<Item = Box<JavaStr>>>(iter: T) -> Self {
2016 JavaString::from_iter(iter).into_boxed_str()
2017 }
2018}
2019
2020impl<'a> FromIterator<Cow<'a, str>> for Box<JavaStr> {
2021 #[inline]
2022 fn from_iter<T: IntoIterator<Item = Cow<'a, str>>>(iter: T) -> Self {
2023 JavaString::from_iter(iter).into_boxed_str()
2024 }
2025}
2026
2027impl<'a> FromIterator<Cow<'a, JavaStr>> for Box<JavaStr> {
2028 #[inline]
2029 fn from_iter<T: IntoIterator<Item = Cow<'a, JavaStr>>>(iter: T) -> Self {
2030 JavaString::from_iter(iter).into_boxed_str()
2031 }
2032}
2033
2034impl Hash for JavaStr {
2035 #[inline]
2036 fn hash<H: Hasher>(&self, state: &mut H) {
2037 state.write(self.as_bytes());
2038 state.write_u8(0xff);
2039 }
2040}
2041
2042impl<I> Index<I> for JavaStr
2043where
2044 I: JavaStrSliceIndex,
2045{
2046 type Output = JavaStr;
2047
2048 #[inline]
2049 fn index(&self, index: I) -> &Self::Output {
2050 index.index(self)
2051 }
2052}
2053
2054impl<I> IndexMut<I> for JavaStr
2055where
2056 I: JavaStrSliceIndex,
2057{
2058 #[inline]
2059 fn index_mut(&mut self, index: I) -> &mut Self::Output {
2060 index.index_mut(self)
2061 }
2062}
2063
2064impl<'b> PartialEq<&'b JavaStr> for Cow<'_, str> {
2065 #[inline]
2066 fn eq(&self, other: &&'b JavaStr) -> bool {
2067 self == *other
2068 }
2069}
2070
2071impl<'b> PartialEq<&'b JavaStr> for Cow<'_, JavaStr> {
2072 #[inline]
2073 fn eq(&self, other: &&'b JavaStr) -> bool {
2074 self == *other
2075 }
2076}
2077
2078impl<'a> PartialEq<Cow<'a, str>> for &JavaStr {
2079 #[inline]
2080 fn eq(&self, other: &Cow<'a, str>) -> bool {
2081 *self == other
2082 }
2083}
2084
2085impl<'a> PartialEq<Cow<'a, str>> for JavaStr {
2086 #[inline]
2087 fn eq(&self, other: &Cow<'a, str>) -> bool {
2088 other == self
2089 }
2090}
2091
2092impl<'a> PartialEq<Cow<'a, JavaStr>> for &JavaStr {
2093 #[inline]
2094 fn eq(&self, other: &Cow<'a, JavaStr>) -> bool {
2095 *self == other
2096 }
2097}
2098
2099impl<'a> PartialEq<Cow<'a, JavaStr>> for JavaStr {
2100 #[inline]
2101 fn eq(&self, other: &Cow<'a, JavaStr>) -> bool {
2102 other == self
2103 }
2104}
2105
2106impl PartialEq<String> for &JavaStr {
2107 #[inline]
2108 fn eq(&self, other: &String) -> bool {
2109 *self == other
2110 }
2111}
2112
2113impl PartialEq<String> for JavaStr {
2114 #[inline]
2115 fn eq(&self, other: &String) -> bool {
2116 self == &other[..]
2117 }
2118}
2119
2120impl PartialEq<JavaStr> for String {
2121 #[inline]
2122 fn eq(&self, other: &JavaStr) -> bool {
2123 &self[..] == other
2124 }
2125}
2126
2127impl PartialEq<JavaString> for &JavaStr {
2128 #[inline]
2129 fn eq(&self, other: &JavaString) -> bool {
2130 *self == other
2131 }
2132}
2133
2134impl PartialEq<JavaString> for JavaStr {
2135 #[inline]
2136 fn eq(&self, other: &JavaString) -> bool {
2137 self == other[..]
2138 }
2139}
2140
2141impl PartialEq<JavaStr> for Cow<'_, str> {
2142 #[inline]
2143 fn eq(&self, other: &JavaStr) -> bool {
2144 match self {
2145 Cow::Borrowed(this) => this == other,
2146 Cow::Owned(this) => this == other,
2147 }
2148 }
2149}
2150
2151impl PartialEq<JavaStr> for Cow<'_, JavaStr> {
2152 #[inline]
2153 fn eq(&self, other: &JavaStr) -> bool {
2154 match self {
2155 Cow::Borrowed(this) => this == other,
2156 Cow::Owned(this) => this == other,
2157 }
2158 }
2159}
2160
2161impl PartialEq<JavaStr> for str {
2162 #[inline]
2163 fn eq(&self, other: &JavaStr) -> bool {
2164 JavaStr::from_str(self) == other
2165 }
2166}
2167
2168impl PartialEq<JavaStr> for &str {
2169 #[inline]
2170 fn eq(&self, other: &JavaStr) -> bool {
2171 self.as_bytes() == &other.inner
2172 }
2173}
2174
2175impl PartialEq<str> for JavaStr {
2176 #[inline]
2177 fn eq(&self, other: &str) -> bool {
2178 &self.inner == other.as_bytes()
2179 }
2180}
2181
2182impl<'a> PartialEq<&'a str> for JavaStr {
2183 #[inline]
2184 fn eq(&self, other: &&'a str) -> bool {
2185 &self.inner == other.as_bytes()
2186 }
2187}
2188
2189impl PartialEq<JavaStr> for &JavaStr {
2190 #[inline]
2191 fn eq(&self, other: &JavaStr) -> bool {
2192 self.inner == other.inner
2193 }
2194}
2195
2196impl<'a> PartialEq<&'a JavaStr> for JavaStr {
2197 #[inline]
2198 fn eq(&self, other: &&'a JavaStr) -> bool {
2199 self.inner == other.inner
2200 }
2201}
2202
2203impl ToOwned for JavaStr {
2204 type Owned = JavaString;
2205
2206 #[inline]
2207 fn to_owned(&self) -> Self::Owned {
2208 unsafe { JavaString::from_semi_utf8_unchecked(self.as_bytes().to_vec()) }
2209 }
2210}
2211
2212mod private_slice_index {
2213 use std::ops;
2214
2215 pub trait Sealed {}
2216
2217 impl Sealed for ops::Range<usize> {}
2218 impl Sealed for ops::RangeTo<usize> {}
2219 impl Sealed for ops::RangeFrom<usize> {}
2220 impl Sealed for ops::RangeFull {}
2221 impl Sealed for ops::RangeInclusive<usize> {}
2222 impl Sealed for ops::RangeToInclusive<usize> {}
2223}
2224
2225/// # Safety
2226///
2227/// Implementations' `check_bounds` method must properly check the bounds of the
2228/// slice, such that calling `get_unchecked` is not UB.
2229pub unsafe trait JavaStrSliceIndex: private_slice_index::Sealed + Sized {
2230 fn check_bounds(&self, slice: &JavaStr) -> bool;
2231 fn check_bounds_fail(self, slice: &JavaStr) -> !;
2232
2233 /// # Safety
2234 ///
2235 /// - The input slice must be a valid pointer
2236 /// - This index must not be out of bounds of the input slice
2237 /// - The indices of this slice must point to char boundaries in the input
2238 /// slice
2239 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr;
2240
2241 /// # Safety
2242 ///
2243 /// - The input slice must be a valid pointer
2244 /// - This index must not be out of bounds of the input slice
2245 /// - The indices of this slice must point to char boundaries in the input
2246 /// slice
2247 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr;
2248
2249 #[inline]
2250 fn get(self, slice: &JavaStr) -> Option<&JavaStr> {
2251 self.check_bounds(slice)
2252 .then(|| unsafe { &*self.get_unchecked(slice) })
2253 }
2254
2255 #[inline]
2256 fn get_mut(self, slice: &mut JavaStr) -> Option<&mut JavaStr> {
2257 self.check_bounds(slice)
2258 .then(|| unsafe { &mut *self.get_unchecked_mut(slice) })
2259 }
2260
2261 #[inline]
2262 fn index(self, slice: &JavaStr) -> &JavaStr {
2263 if self.check_bounds(slice) {
2264 unsafe { &*self.get_unchecked(slice) }
2265 } else {
2266 self.check_bounds_fail(slice)
2267 }
2268 }
2269
2270 #[inline]
2271 fn index_mut(self, slice: &mut JavaStr) -> &mut JavaStr {
2272 if self.check_bounds(slice) {
2273 unsafe { &mut *self.get_unchecked_mut(slice) }
2274 } else {
2275 self.check_bounds_fail(slice)
2276 }
2277 }
2278}
2279
2280unsafe impl JavaStrSliceIndex for RangeFull {
2281 #[inline]
2282 fn check_bounds(&self, _slice: &JavaStr) -> bool {
2283 true
2284 }
2285
2286 #[inline]
2287 fn check_bounds_fail(self, _slice: &JavaStr) -> ! {
2288 unreachable!()
2289 }
2290
2291 #[inline]
2292 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2293 slice
2294 }
2295
2296 #[inline]
2297 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2298 slice
2299 }
2300}
2301
2302unsafe impl JavaStrSliceIndex for Range<usize> {
2303 #[inline]
2304 fn check_bounds(&self, slice: &JavaStr) -> bool {
2305 self.start <= self.end
2306 && slice.is_char_boundary(self.start)
2307 && slice.is_char_boundary(self.end)
2308 }
2309
2310 #[inline]
2311 #[track_caller]
2312 fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2313 slice_error_fail(slice, self.start, self.end)
2314 }
2315
2316 #[inline]
2317 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2318 let slice = slice as *const [u8];
2319 // SAFETY: the caller guarantees that `self` is in bounds of `slice`
2320 // which satisfies all the conditions for `add`.
2321 let ptr = unsafe { (slice as *const u8).add(self.start) };
2322 let len = self.end - self.start;
2323 ptr::slice_from_raw_parts(ptr, len) as *const JavaStr
2324 }
2325
2326 #[inline]
2327 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2328 let slice = slice as *mut [u8];
2329 // SAFETY: see comments for `get_unchecked`.
2330 let ptr = unsafe { (slice as *mut u8).add(self.start) };
2331 let len = self.end - self.start;
2332 ptr::slice_from_raw_parts_mut(ptr, len) as *mut JavaStr
2333 }
2334}
2335
2336unsafe impl JavaStrSliceIndex for RangeTo<usize> {
2337 #[inline]
2338 fn check_bounds(&self, slice: &JavaStr) -> bool {
2339 slice.is_char_boundary(self.end)
2340 }
2341
2342 #[inline]
2343 #[track_caller]
2344 fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2345 slice_error_fail(slice, 0, self.end)
2346 }
2347
2348 #[inline]
2349 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2350 unsafe { (0..self.end).get_unchecked(slice) }
2351 }
2352
2353 #[inline]
2354 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2355 unsafe { (0..self.end).get_unchecked_mut(slice) }
2356 }
2357}
2358
2359unsafe impl JavaStrSliceIndex for RangeFrom<usize> {
2360 #[inline]
2361 fn check_bounds(&self, slice: &JavaStr) -> bool {
2362 slice.is_char_boundary(self.start)
2363 }
2364
2365 #[inline]
2366 #[track_caller]
2367 fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2368 slice_error_fail(slice, self.start, slice.len())
2369 }
2370
2371 #[inline]
2372 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2373 let len = unsafe { (&(*(slice as *const [u8]))).len() };
2374 #[allow(clippy::needless_borrow)]
2375 unsafe {
2376 (self.start..len).get_unchecked(slice)
2377 }
2378 }
2379
2380 #[inline]
2381 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2382 let len = unsafe { (&(*(slice as *mut [u8]))).len() };
2383 #[allow(clippy::needless_borrow)]
2384 unsafe {
2385 (self.start..len).get_unchecked_mut(slice)
2386 }
2387 }
2388}
2389
2390#[inline]
2391fn into_slice_range(range: RangeInclusive<usize>) -> Range<usize> {
2392 let exclusive_end = *range.end() + 1;
2393 let start = match range.end_bound() {
2394 Bound::Excluded(..) => exclusive_end, // excluded
2395 Bound::Included(..) => *range.start(),
2396 Bound::Unbounded => unreachable!(),
2397 };
2398 start..exclusive_end
2399}
2400
2401unsafe impl JavaStrSliceIndex for RangeInclusive<usize> {
2402 #[inline]
2403 fn check_bounds(&self, slice: &JavaStr) -> bool {
2404 *self.end() != usize::MAX && into_slice_range(self.clone()).check_bounds(slice)
2405 }
2406
2407 #[inline]
2408 #[track_caller]
2409 fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2410 if *self.end() == usize::MAX {
2411 str_end_index_overflow_fail()
2412 } else {
2413 into_slice_range(self).check_bounds_fail(slice)
2414 }
2415 }
2416
2417 #[inline]
2418 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2419 into_slice_range(self).get_unchecked(slice)
2420 }
2421
2422 #[inline]
2423 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2424 into_slice_range(self).get_unchecked_mut(slice)
2425 }
2426}
2427
2428unsafe impl JavaStrSliceIndex for RangeToInclusive<usize> {
2429 #[inline]
2430 fn check_bounds(&self, slice: &JavaStr) -> bool {
2431 (0..=self.end).check_bounds(slice)
2432 }
2433
2434 #[inline]
2435 fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2436 (0..=self.end).check_bounds_fail(slice)
2437 }
2438
2439 #[inline]
2440 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2441 (0..=self.end).get_unchecked(slice)
2442 }
2443
2444 #[inline]
2445 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2446 (0..=self.end).get_unchecked_mut(slice)
2447 }
2448}