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()
489 // Empty strings are valid, so return true
490 // - index > self.len()
491 // In this case return false
492 //
493 // The check is placed exactly here, because it improves generated
494 // code on higher opt-levels. See PR https://github.com/rust-lang/rust/pull/84751 for more details.
495 index == self.len()
496 } else {
497 // This is bit magic equivalent to: b < 128 || b >= 192
498 (self.as_bytes()[index] as i8) >= -0x40
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 const 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 // SAFETY: just checked that `mid` is on a char boundary.
1164 unsafe { self.split_at_mut_unchecked(mid) }
1165 } else {
1166 slice_error_fail(self, 0, mid)
1167 }
1168 }
1169
1170 /// See [`str::split_at_checked`].
1171 ///
1172 /// ```
1173 /// # use java_string::JavaStr;
1174 /// let s = JavaStr::from_str("Per Martin-Löf");
1175 ///
1176 /// let (first, last) = s.split_at_checked(3).unwrap();
1177 /// assert_eq!("Per", first);
1178 /// assert_eq!(" Martin-Löf", last);
1179 ///
1180 /// assert_eq!(None, s.split_at_checked(13)); // Inside “ö”
1181 /// assert_eq!(None, s.split_at_checked(16)); // Beyond the string length
1182 /// ```
1183 #[inline]
1184 #[must_use]
1185 pub const fn split_at_checked(&self, mid: usize) -> Option<(&JavaStr, &JavaStr)> {
1186 // is_char_boundary checks that the index is in [0, .len()]
1187 if self.is_char_boundary(mid) {
1188 // SAFETY: just checked that `mid` is on a char boundary.
1189 unsafe { Some(self.split_at_unchecked(mid)) }
1190 } else {
1191 None
1192 }
1193 }
1194
1195 /// # Safety
1196 ///
1197 /// Caller must ensure that `mid` lies on a valid char boundary
1198 #[inline]
1199 const unsafe fn split_at_unchecked(&self, mid: usize) -> (&JavaStr, &JavaStr) {
1200 let len = self.len();
1201 let ptr = self.as_ptr();
1202 // SAFETY: caller guarantees `mid` is on a char boundary.
1203 unsafe {
1204 (
1205 Self::from_semi_utf8_unchecked(slice::from_raw_parts(ptr, mid)),
1206 Self::from_semi_utf8_unchecked(slice::from_raw_parts(ptr.add(mid), len - mid)),
1207 )
1208 }
1209 }
1210
1211 /// See [`str::split_at_mut_checked`].
1212 ///
1213 /// ```
1214 /// # use java_string::{JavaStr, JavaString};
1215 /// let mut s = JavaString::from("Per Martin-Löf");
1216 /// let mut s = s.as_mut_java_str();
1217 /// if let Some((first, last)) = s.split_at_mut_checked(3) {
1218 /// first.make_ascii_uppercase();
1219 /// assert_eq!("PER", first);
1220 /// assert_eq!(" Martin-Löf", last);
1221 /// }
1222 /// assert_eq!("PER Martin-Löf", s);
1223 ///
1224 /// assert_eq!(None, s.split_at_mut_checked(13)); // Inside “ö”
1225 /// assert_eq!(None, s.split_at_mut_checked(16)); // Beyond the string length
1226 /// ```
1227 #[inline]
1228 #[must_use]
1229 pub const fn split_at_mut_checked(
1230 &mut self,
1231 mid: usize,
1232 ) -> Option<(&mut JavaStr, &mut JavaStr)> {
1233 // is_char_boundary checks that the index is in [0, .len()]
1234 if self.is_char_boundary(mid) {
1235 // SAFETY: just checked that `mid` is on a char boundary.
1236 unsafe { Some(self.split_at_mut_unchecked(mid)) }
1237 } else {
1238 None
1239 }
1240 }
1241
1242 /// # Safety
1243 ///
1244 /// Caller must ensure that `mid` lies on a valid char boundary
1245 #[inline]
1246 const unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut JavaStr, &mut JavaStr) {
1247 let len = self.len();
1248 let ptr = self.as_mut_ptr();
1249 // SAFETY: caller guarantees `mid` is on a char boundary.
1250 unsafe {
1251 (
1252 Self::from_semi_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)),
1253 Self::from_semi_utf8_unchecked_mut(slice::from_raw_parts_mut(
1254 ptr.add(mid),
1255 len - mid,
1256 )),
1257 )
1258 }
1259 }
1260
1261 /// See [`str::split_inclusive`].
1262 ///
1263 /// ```
1264 /// # use java_string::JavaStr;
1265 /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lamb\nlittle lamb\nlittle lamb.\n")
1266 /// .split_inclusive('\n')
1267 /// .collect();
1268 /// assert_eq!(
1269 /// v,
1270 /// [
1271 /// JavaStr::from_str("Mary had a little lamb\n"),
1272 /// JavaStr::from_str("little lamb\n"),
1273 /// JavaStr::from_str("little lamb.\n")
1274 /// ]
1275 /// );
1276 /// ```
1277 #[inline]
1278 pub fn split_inclusive<P>(&self, pat: P) -> SplitInclusive<'_, P>
1279 where
1280 P: JavaStrPattern,
1281 {
1282 SplitInclusive::new(self, pat)
1283 }
1284
1285 /// See [`str::split_once`].
1286 ///
1287 /// ```
1288 /// # use java_string::JavaStr;
1289 /// assert_eq!(JavaStr::from_str("cfg").split_once('='), None);
1290 /// assert_eq!(
1291 /// JavaStr::from_str("cfg=").split_once('='),
1292 /// Some((JavaStr::from_str("cfg"), JavaStr::from_str("")))
1293 /// );
1294 /// assert_eq!(
1295 /// JavaStr::from_str("cfg=foo").split_once('='),
1296 /// Some((JavaStr::from_str("cfg"), JavaStr::from_str("foo")))
1297 /// );
1298 /// assert_eq!(
1299 /// JavaStr::from_str("cfg=foo=bar").split_once('='),
1300 /// Some((JavaStr::from_str("cfg"), JavaStr::from_str("foo=bar")))
1301 /// );
1302 /// ```
1303 #[inline]
1304 #[must_use]
1305 pub fn split_once<P>(&self, mut delimiter: P) -> Option<(&JavaStr, &JavaStr)>
1306 where
1307 P: JavaStrPattern,
1308 {
1309 let (index, len) = delimiter.find_in(self)?;
1310 // SAFETY: pattern is known to return valid indices.
1311 unsafe {
1312 Some((
1313 self.get_unchecked(..index),
1314 self.get_unchecked(index + len..),
1315 ))
1316 }
1317 }
1318
1319 /// See [`str::split_terminator`].
1320 ///
1321 /// ```
1322 /// # use java_string::JavaStr;
1323 /// let v: Vec<&JavaStr> = JavaStr::from_str("A.B.").split_terminator('.').collect();
1324 /// assert_eq!(v, [JavaStr::from_str("A"), JavaStr::from_str("B")]);
1325 ///
1326 /// let v: Vec<&JavaStr> = JavaStr::from_str("A..B..").split_terminator(".").collect();
1327 /// assert_eq!(
1328 /// v,
1329 /// [
1330 /// JavaStr::from_str("A"),
1331 /// JavaStr::from_str(""),
1332 /// JavaStr::from_str("B"),
1333 /// JavaStr::from_str("")
1334 /// ]
1335 /// );
1336 ///
1337 /// let v: Vec<&JavaStr> = JavaStr::from_str("A.B:C.D")
1338 /// .split_terminator(&['.', ':'][..])
1339 /// .collect();
1340 /// assert_eq!(
1341 /// v,
1342 /// [
1343 /// JavaStr::from_str("A"),
1344 /// JavaStr::from_str("B"),
1345 /// JavaStr::from_str("C"),
1346 /// JavaStr::from_str("D")
1347 /// ]
1348 /// );
1349 /// ```
1350 #[inline]
1351 pub fn split_terminator<P>(&self, pat: P) -> SplitTerminator<'_, P>
1352 where
1353 P: JavaStrPattern,
1354 {
1355 SplitTerminator::new(self, pat)
1356 }
1357
1358 /// See [`str::split_whitespace`].
1359 #[inline]
1360 pub fn split_whitespace(&self) -> SplitWhitespace<'_> {
1361 SplitWhitespace {
1362 inner: self
1363 .split(JavaCodePoint::is_whitespace as fn(JavaCodePoint) -> bool)
1364 .filter(|str| !str.is_empty()),
1365 }
1366 }
1367
1368 /// See [`str::splitn`].
1369 ///
1370 /// ```
1371 /// # use java_string::JavaStr;
1372 /// let v: Vec<&JavaStr> = JavaStr::from_str("Mary had a little lambda")
1373 /// .splitn(3, ' ')
1374 /// .collect();
1375 /// assert_eq!(
1376 /// v,
1377 /// [
1378 /// JavaStr::from_str("Mary"),
1379 /// JavaStr::from_str("had"),
1380 /// JavaStr::from_str("a little lambda")
1381 /// ]
1382 /// );
1383 ///
1384 /// let v: Vec<&JavaStr> = JavaStr::from_str("lionXXtigerXleopard")
1385 /// .splitn(3, "X")
1386 /// .collect();
1387 /// assert_eq!(
1388 /// v,
1389 /// [
1390 /// JavaStr::from_str("lion"),
1391 /// JavaStr::from_str(""),
1392 /// JavaStr::from_str("tigerXleopard")
1393 /// ]
1394 /// );
1395 ///
1396 /// let v: Vec<&JavaStr> = JavaStr::from_str("abcXdef").splitn(1, 'X').collect();
1397 /// assert_eq!(v, [JavaStr::from_str("abcXdef")]);
1398 ///
1399 /// let v: Vec<&JavaStr> = JavaStr::from_str("").splitn(1, 'X').collect();
1400 /// assert_eq!(v, [JavaStr::from_str("")]);
1401 /// ```
1402 #[inline]
1403 pub fn splitn<P>(&self, n: usize, pat: P) -> SplitN<'_, P>
1404 where
1405 P: JavaStrPattern,
1406 {
1407 SplitN::new(self, pat, n)
1408 }
1409
1410 /// See [`str::starts_with`].
1411 ///
1412 /// ```
1413 /// # use java_string::JavaStr;
1414 /// let bananas = JavaStr::from_str("bananas");
1415 ///
1416 /// assert!(bananas.starts_with("bana"));
1417 /// assert!(!bananas.starts_with("nana"));
1418 /// ```
1419 #[inline]
1420 #[must_use]
1421 pub fn starts_with<P>(&self, mut pat: P) -> bool
1422 where
1423 P: JavaStrPattern,
1424 {
1425 pat.prefix_len_in(self).is_some()
1426 }
1427
1428 /// See [`str::strip_prefix`].
1429 ///
1430 /// ```
1431 /// # use java_string::JavaStr;
1432 /// assert_eq!(
1433 /// JavaStr::from_str("foo:bar").strip_prefix("foo:"),
1434 /// Some(JavaStr::from_str("bar"))
1435 /// );
1436 /// assert_eq!(JavaStr::from_str("foo:bar").strip_prefix("bar"), None);
1437 /// assert_eq!(
1438 /// JavaStr::from_str("foofoo").strip_prefix("foo"),
1439 /// Some(JavaStr::from_str("foo"))
1440 /// );
1441 /// ```
1442 #[inline]
1443 #[must_use]
1444 pub fn strip_prefix<P>(&self, mut prefix: P) -> Option<&JavaStr>
1445 where
1446 P: JavaStrPattern,
1447 {
1448 let len = prefix.prefix_len_in(self)?;
1449 // SAFETY: pattern is known to return valid indices.
1450 unsafe { Some(self.get_unchecked(len..)) }
1451 }
1452
1453 /// See [`str::strip_suffix`].
1454 ///
1455 /// ```
1456 /// # use java_string::JavaStr;
1457 /// assert_eq!(
1458 /// JavaStr::from_str("bar:foo").strip_suffix(":foo"),
1459 /// Some(JavaStr::from_str("bar"))
1460 /// );
1461 /// assert_eq!(JavaStr::from_str("bar:foo").strip_suffix("bar"), None);
1462 /// assert_eq!(
1463 /// JavaStr::from_str("foofoo").strip_suffix("foo"),
1464 /// Some(JavaStr::from_str("foo"))
1465 /// );
1466 /// ```
1467 #[inline]
1468 #[must_use]
1469 pub fn strip_suffix<P>(&self, mut suffix: P) -> Option<&JavaStr>
1470 where
1471 P: JavaStrPattern,
1472 {
1473 let len = suffix.suffix_len_in(self)?;
1474 // SAFETY: pattern is known to return valid indices.
1475 unsafe { Some(self.get_unchecked(..self.len() - len)) }
1476 }
1477
1478 /// See [`str::to_ascii_lowercase`].
1479 #[inline]
1480 #[must_use]
1481 pub fn to_ascii_lowercase(&self) -> JavaString {
1482 let mut s = self.to_owned();
1483 s.make_ascii_lowercase();
1484 s
1485 }
1486
1487 /// See [`str::to_ascii_uppercase`].
1488 #[inline]
1489 #[must_use]
1490 pub fn to_ascii_uppercase(&self) -> JavaString {
1491 let mut s = self.to_owned();
1492 s.make_ascii_uppercase();
1493 s
1494 }
1495
1496 /// See [`str::to_lowercase`].
1497 ///
1498 /// ```
1499 /// # use java_string::{JavaCodePoint, JavaStr, JavaString};
1500 /// let s = JavaStr::from_str("HELLO");
1501 /// assert_eq!("hello", s.to_lowercase());
1502 ///
1503 /// let odysseus = JavaStr::from_str("ὈΔΥΣΣΕΎΣ");
1504 /// assert_eq!("ὀδυσσεύς", odysseus.to_lowercase());
1505 ///
1506 /// let s = JavaString::from("Hello ")
1507 /// + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1508 /// + JavaStr::from_str(" World!");
1509 /// let expected = JavaString::from("hello ")
1510 /// + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1511 /// + JavaStr::from_str(" world!");
1512 /// assert_eq!(expected, s.to_lowercase());
1513 /// ```
1514 #[inline]
1515 #[must_use]
1516 pub fn to_lowercase(&self) -> JavaString {
1517 self.transform_string(str::to_lowercase, |ch| ch)
1518 }
1519
1520 /// See [`str::to_uppercase`].
1521 ///
1522 /// ```
1523 /// # use java_string::{JavaCodePoint, JavaStr, JavaString};
1524 /// let s = JavaStr::from_str("hello");
1525 /// assert_eq!("HELLO", s.to_uppercase());
1526 ///
1527 /// let s = JavaStr::from_str("tschüß");
1528 /// assert_eq!("TSCHÜSS", s.to_uppercase());
1529 ///
1530 /// let s = JavaString::from("Hello ")
1531 /// + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1532 /// + JavaStr::from_str(" World!");
1533 /// let expected = JavaString::from("HELLO ")
1534 /// + JavaString::from(JavaCodePoint::from_u32(0xd800).unwrap()).as_java_str()
1535 /// + JavaStr::from_str(" WORLD!");
1536 /// assert_eq!(expected, s.to_uppercase());
1537 /// ```
1538 #[inline]
1539 #[must_use]
1540 pub fn to_uppercase(&self) -> JavaString {
1541 self.transform_string(str::to_uppercase, |ch| ch)
1542 }
1543
1544 /// See [`str::trim`].
1545 #[inline]
1546 #[must_use]
1547 pub fn trim(&self) -> &JavaStr {
1548 self.trim_matches(|c: JavaCodePoint| c.is_whitespace())
1549 }
1550
1551 /// See [`str::trim_ascii`]
1552 #[inline]
1553 #[must_use]
1554 pub fn trim_ascii(&self) -> &JavaStr {
1555 self.trim_matches(|c: JavaCodePoint| c.is_ascii_whitespace())
1556 }
1557
1558 /// See [`str::trim_end`].
1559 #[inline]
1560 #[must_use]
1561 pub fn trim_end(&self) -> &JavaStr {
1562 self.trim_end_matches(|c: JavaCodePoint| c.is_whitespace())
1563 }
1564
1565 /// See [`str::trim_ascii_end`]
1566 pub fn trim_ascii_end(&self) -> &JavaStr {
1567 self.trim_end_matches(|c: JavaCodePoint| c.is_ascii_whitespace())
1568 }
1569
1570 /// See [`str::trim_end_matches`].
1571 ///
1572 /// ```
1573 /// # use java_string::{JavaCodePoint, JavaStr};
1574 /// assert_eq!(
1575 /// JavaStr::from_str("11foo1bar11").trim_end_matches('1'),
1576 /// "11foo1bar"
1577 /// );
1578 /// assert_eq!(
1579 /// JavaStr::from_str("123foo1bar123").trim_end_matches(JavaCodePoint::is_numeric),
1580 /// "123foo1bar"
1581 /// );
1582 ///
1583 /// let x: &[_] = &['1', '2'];
1584 /// assert_eq!(
1585 /// JavaStr::from_str("12foo1bar12").trim_end_matches(x),
1586 /// "12foo1bar"
1587 /// );
1588 /// ```
1589 #[inline]
1590 #[must_use]
1591 pub fn trim_end_matches<P>(&self, mut pat: P) -> &JavaStr
1592 where
1593 P: JavaStrPattern,
1594 {
1595 let mut str = self;
1596 while let Some(suffix_len) = pat.suffix_len_in(str) {
1597 if suffix_len == 0 {
1598 break;
1599 }
1600 // SAFETY: pattern is known to return valid indices.
1601 str = unsafe { str.get_unchecked(..str.len() - suffix_len) };
1602 }
1603 str
1604 }
1605
1606 /// See [`str::trim_matches`].
1607 ///
1608 /// ```
1609 /// # use java_string::{JavaCodePoint, JavaStr};
1610 /// assert_eq!(
1611 /// JavaStr::from_str("11foo1bar11").trim_matches('1'),
1612 /// "foo1bar"
1613 /// );
1614 /// assert_eq!(
1615 /// JavaStr::from_str("123foo1bar123").trim_matches(JavaCodePoint::is_numeric),
1616 /// "foo1bar"
1617 /// );
1618 ///
1619 /// let x: &[_] = &['1', '2'];
1620 /// assert_eq!(JavaStr::from_str("12foo1bar12").trim_matches(x), "foo1bar");
1621 /// ```
1622 #[inline]
1623 #[must_use]
1624 pub fn trim_matches<P>(&self, mut pat: P) -> &JavaStr
1625 where
1626 P: JavaStrPattern,
1627 {
1628 let mut str = self;
1629 while let Some(prefix_len) = pat.prefix_len_in(str) {
1630 if prefix_len == 0 {
1631 break;
1632 }
1633 // SAFETY: pattern is known to return valid indices.
1634 str = unsafe { str.get_unchecked(prefix_len..) };
1635 }
1636 while let Some(suffix_len) = pat.suffix_len_in(str) {
1637 if suffix_len == 0 {
1638 break;
1639 }
1640 // SAFETY: pattern is known to return valid indices.
1641 str = unsafe { str.get_unchecked(..str.len() - suffix_len) };
1642 }
1643 str
1644 }
1645
1646 /// See [`str::trim_start`].
1647 #[inline]
1648 #[must_use]
1649 pub fn trim_start(&self) -> &JavaStr {
1650 self.trim_start_matches(|c: JavaCodePoint| c.is_whitespace())
1651 }
1652
1653 /// See [`str::trim_ascii_start`]
1654 #[inline]
1655 #[must_use]
1656 pub fn trim_ascii_start(&self) -> &JavaStr {
1657 self.trim_start_matches(|c: JavaCodePoint| c.is_ascii_whitespace())
1658 }
1659
1660 /// See [`str::trim_start_matches`].
1661 ///
1662 /// ```
1663 /// # use java_string::{JavaCodePoint, JavaStr};
1664 /// assert_eq!(
1665 /// JavaStr::from_str("11foo1bar11").trim_start_matches('1'),
1666 /// "foo1bar11"
1667 /// );
1668 /// assert_eq!(
1669 /// JavaStr::from_str("123foo1bar123").trim_start_matches(JavaCodePoint::is_numeric),
1670 /// "foo1bar123"
1671 /// );
1672 ///
1673 /// let x: &[_] = &['1', '2'];
1674 /// assert_eq!(
1675 /// JavaStr::from_str("12foo1bar12").trim_start_matches(x),
1676 /// "foo1bar12"
1677 /// );
1678 /// ```
1679 #[inline]
1680 #[must_use]
1681 pub fn trim_start_matches<P>(&self, mut pat: P) -> &JavaStr
1682 where
1683 P: JavaStrPattern,
1684 {
1685 let mut str = self;
1686 while let Some(prefix_len) = pat.prefix_len_in(str) {
1687 if prefix_len == 0 {
1688 break;
1689 }
1690 // SAFETY: pattern is known to return valid indices.
1691 str = unsafe { str.get_unchecked(prefix_len..) };
1692 }
1693 str
1694 }
1695
1696 #[inline]
1697 fn transform_string<SF, ICF>(
1698 &self,
1699 mut string_transformer: SF,
1700 invalid_char_transformer: ICF,
1701 ) -> JavaString
1702 where
1703 SF: FnMut(&str) -> String,
1704 ICF: FnMut(&JavaStr) -> &JavaStr,
1705 {
1706 let bytes = self.as_bytes();
1707 match run_utf8_full_validation_from_semi(bytes) {
1708 Ok(()) => JavaString::from(string_transformer(unsafe {
1709 // SAFETY: validation succeeded
1710 std::str::from_utf8_unchecked(bytes)
1711 })),
1712 Err(error) => {
1713 self.transform_invalid_string(error, string_transformer, invalid_char_transformer)
1714 }
1715 }
1716 }
1717
1718 #[inline]
1719 fn transform_invalid_string<SF, ICF>(
1720 &self,
1721 error: Utf8Error,
1722 mut string_transformer: SF,
1723 mut invalid_char_transformer: ICF,
1724 ) -> JavaString
1725 where
1726 SF: FnMut(&str) -> String,
1727 ICF: FnMut(&JavaStr) -> &JavaStr,
1728 {
1729 let bytes = self.as_bytes();
1730 let mut result = JavaString::from(string_transformer(unsafe {
1731 // SAFETY: validation succeeded up to this index
1732 std::str::from_utf8_unchecked(bytes.get_unchecked(..error.valid_up_to))
1733 }));
1734 result.push_java_str(invalid_char_transformer(unsafe {
1735 // SAFETY: any UTF-8 error in semi-valid UTF-8 is a 3 byte long sequence
1736 // representing a surrogate code point. We're pushing that sequence now
1737 JavaStr::from_semi_utf8_unchecked(
1738 bytes.get_unchecked(error.valid_up_to..error.valid_up_to + 3),
1739 )
1740 }));
1741 let mut index = error.valid_up_to + 3;
1742 loop {
1743 let remainder = unsafe { bytes.get_unchecked(index..) };
1744 match run_utf8_full_validation_from_semi(remainder) {
1745 Ok(()) => {
1746 result.push_str(&string_transformer(unsafe {
1747 // SAFETY: validation succeeded
1748 std::str::from_utf8_unchecked(remainder)
1749 }));
1750 return result;
1751 }
1752 Err(error) => {
1753 result.push_str(&string_transformer(unsafe {
1754 // SAFETY: validation succeeded up to this index
1755 std::str::from_utf8_unchecked(
1756 bytes.get_unchecked(index..index + error.valid_up_to),
1757 )
1758 }));
1759 result.push_java_str(invalid_char_transformer(unsafe {
1760 // SAFETY: see comment above
1761 JavaStr::from_semi_utf8_unchecked(bytes.get_unchecked(
1762 index + error.valid_up_to..index + error.valid_up_to + 3,
1763 ))
1764 }));
1765 index += error.valid_up_to + 3;
1766 }
1767 }
1768 }
1769 }
1770}
1771
1772impl<'a> Add<&JavaStr> for Cow<'a, JavaStr> {
1773 type Output = Cow<'a, JavaStr>;
1774
1775 #[inline]
1776 fn add(mut self, rhs: &JavaStr) -> Self::Output {
1777 self += rhs;
1778 self
1779 }
1780}
1781
1782impl AddAssign<&JavaStr> for Cow<'_, JavaStr> {
1783 #[inline]
1784 fn add_assign(&mut self, rhs: &JavaStr) {
1785 if !rhs.is_empty() {
1786 match self {
1787 Cow::Borrowed(lhs) => {
1788 let mut result = lhs.to_owned();
1789 result.push_java_str(rhs);
1790 *self = Cow::Owned(result);
1791 }
1792 Cow::Owned(lhs) => {
1793 lhs.push_java_str(rhs);
1794 }
1795 }
1796 }
1797 }
1798}
1799
1800impl AsRef<[u8]> for JavaStr {
1801 #[inline]
1802 fn as_ref(&self) -> &[u8] {
1803 self.as_bytes()
1804 }
1805}
1806
1807impl AsRef<JavaStr> for str {
1808 #[inline]
1809 fn as_ref(&self) -> &JavaStr {
1810 JavaStr::from_str(self)
1811 }
1812}
1813
1814impl AsRef<JavaStr> for String {
1815 #[inline]
1816 fn as_ref(&self) -> &JavaStr {
1817 JavaStr::from_str(self)
1818 }
1819}
1820
1821impl AsRef<JavaStr> for JavaStr {
1822 #[inline]
1823 fn as_ref(&self) -> &JavaStr {
1824 self
1825 }
1826}
1827
1828impl Clone for Box<JavaStr> {
1829 #[inline]
1830 fn clone(&self) -> Self {
1831 let buf: Box<[u8]> = self.as_bytes().into();
1832 unsafe { JavaStr::from_boxed_semi_utf8_unchecked(buf) }
1833 }
1834}
1835
1836impl Debug for JavaStr {
1837 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1838 f.write_char('"')?;
1839 let mut from = 0;
1840 for (i, c) in self.char_indices() {
1841 let esc = c.escape_debug_ext(EscapeDebugExtArgs {
1842 escape_single_quote: false,
1843 escape_double_quote: true,
1844 });
1845 // If char needs escaping, flush backlog so far and write, else skip.
1846 // Also handle invalid UTF-8 here
1847 if esc.len() != 1 || c.as_char().is_none() {
1848 unsafe {
1849 // SAFETY: any invalid UTF-8 should have been caught by a previous iteration
1850 f.write_str(self[from..i].as_str_unchecked())?
1851 };
1852 for c in esc {
1853 f.write_char(c)?;
1854 }
1855 from = i + c.len_utf8();
1856 }
1857 }
1858 unsafe {
1859 // SAFETY: any invalid UTF-8 should have been caught by the loop above
1860 f.write_str(self[from..].as_str_unchecked())?
1861 };
1862 f.write_char('"')
1863 }
1864}
1865
1866impl Default for &JavaStr {
1867 #[inline]
1868 fn default() -> Self {
1869 JavaStr::from_str("")
1870 }
1871}
1872
1873impl Default for Box<JavaStr> {
1874 #[inline]
1875 fn default() -> Self {
1876 JavaStr::from_boxed_str(Box::<str>::default())
1877 }
1878}
1879
1880impl Display for JavaStr {
1881 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1882 Display::fmt(&self.as_str_lossy(), f)
1883 }
1884}
1885
1886impl<'a> From<&'a JavaStr> for Cow<'a, JavaStr> {
1887 #[inline]
1888 fn from(value: &'a JavaStr) -> Self {
1889 Cow::Borrowed(value)
1890 }
1891}
1892
1893impl From<&JavaStr> for Arc<JavaStr> {
1894 #[inline]
1895 fn from(value: &JavaStr) -> Self {
1896 let arc = Arc::<[u8]>::from(value.as_bytes());
1897 unsafe { Arc::from_raw(Arc::into_raw(arc) as *const JavaStr) }
1898 }
1899}
1900
1901impl From<&JavaStr> for Box<JavaStr> {
1902 #[inline]
1903 fn from(value: &JavaStr) -> Self {
1904 unsafe { JavaStr::from_boxed_semi_utf8_unchecked(Box::from(value.as_bytes())) }
1905 }
1906}
1907
1908impl From<&JavaStr> for Rc<JavaStr> {
1909 #[inline]
1910 fn from(value: &JavaStr) -> Self {
1911 let rc = Rc::<[u8]>::from(value.as_bytes());
1912 unsafe { Rc::from_raw(Rc::into_raw(rc) as *const JavaStr) }
1913 }
1914}
1915
1916impl From<&JavaStr> for Vec<u8> {
1917 #[inline]
1918 fn from(value: &JavaStr) -> Self {
1919 From::from(value.as_bytes())
1920 }
1921}
1922
1923impl From<Cow<'_, JavaStr>> for Box<JavaStr> {
1924 #[inline]
1925 fn from(value: Cow<'_, JavaStr>) -> Self {
1926 match value {
1927 Cow::Borrowed(s) => Box::from(s),
1928 Cow::Owned(s) => Box::from(s),
1929 }
1930 }
1931}
1932
1933impl From<JavaString> for Box<JavaStr> {
1934 #[inline]
1935 fn from(value: JavaString) -> Self {
1936 value.into_boxed_str()
1937 }
1938}
1939
1940impl<'a> From<&'a str> for &'a JavaStr {
1941 #[inline]
1942 fn from(value: &'a str) -> Self {
1943 JavaStr::from_str(value)
1944 }
1945}
1946
1947impl<'a> From<&'a String> for &'a JavaStr {
1948 #[inline]
1949 fn from(value: &'a String) -> Self {
1950 JavaStr::from_str(value)
1951 }
1952}
1953
1954impl FromIterator<char> for Box<JavaStr> {
1955 #[inline]
1956 fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
1957 JavaString::from_iter(iter).into_boxed_str()
1958 }
1959}
1960
1961impl<'a> FromIterator<&'a char> for Box<JavaStr> {
1962 #[inline]
1963 fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
1964 JavaString::from_iter(iter).into_boxed_str()
1965 }
1966}
1967
1968impl FromIterator<JavaCodePoint> for Box<JavaStr> {
1969 #[inline]
1970 fn from_iter<T: IntoIterator<Item = JavaCodePoint>>(iter: T) -> Self {
1971 JavaString::from_iter(iter).into_boxed_str()
1972 }
1973}
1974
1975impl<'a> FromIterator<&'a JavaCodePoint> for Box<JavaStr> {
1976 #[inline]
1977 fn from_iter<T: IntoIterator<Item = &'a JavaCodePoint>>(iter: T) -> Self {
1978 JavaString::from_iter(iter).into_boxed_str()
1979 }
1980}
1981
1982impl<'a> FromIterator<&'a str> for Box<JavaStr> {
1983 #[inline]
1984 fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
1985 JavaString::from_iter(iter).into_boxed_str()
1986 }
1987}
1988
1989impl<'a> FromIterator<&'a JavaStr> for Box<JavaStr> {
1990 #[inline]
1991 fn from_iter<T: IntoIterator<Item = &'a JavaStr>>(iter: T) -> Self {
1992 JavaString::from_iter(iter).into_boxed_str()
1993 }
1994}
1995
1996impl FromIterator<String> for Box<JavaStr> {
1997 fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
1998 JavaString::from_iter(iter).into_boxed_str()
1999 }
2000}
2001
2002impl FromIterator<JavaString> for Box<JavaStr> {
2003 fn from_iter<T: IntoIterator<Item = JavaString>>(iter: T) -> Self {
2004 JavaString::from_iter(iter).into_boxed_str()
2005 }
2006}
2007
2008impl FromIterator<Box<str>> for Box<JavaStr> {
2009 #[inline]
2010 fn from_iter<T: IntoIterator<Item = Box<str>>>(iter: T) -> Self {
2011 JavaString::from_iter(iter).into_boxed_str()
2012 }
2013}
2014
2015impl FromIterator<Box<JavaStr>> for Box<JavaStr> {
2016 #[inline]
2017 fn from_iter<T: IntoIterator<Item = Box<JavaStr>>>(iter: T) -> Self {
2018 JavaString::from_iter(iter).into_boxed_str()
2019 }
2020}
2021
2022impl<'a> FromIterator<Cow<'a, str>> for Box<JavaStr> {
2023 #[inline]
2024 fn from_iter<T: IntoIterator<Item = Cow<'a, str>>>(iter: T) -> Self {
2025 JavaString::from_iter(iter).into_boxed_str()
2026 }
2027}
2028
2029impl<'a> FromIterator<Cow<'a, JavaStr>> for Box<JavaStr> {
2030 #[inline]
2031 fn from_iter<T: IntoIterator<Item = Cow<'a, JavaStr>>>(iter: T) -> Self {
2032 JavaString::from_iter(iter).into_boxed_str()
2033 }
2034}
2035
2036impl Hash for JavaStr {
2037 #[inline]
2038 fn hash<H: Hasher>(&self, state: &mut H) {
2039 state.write(self.as_bytes());
2040 state.write_u8(0xff);
2041 }
2042}
2043
2044impl<I> Index<I> for JavaStr
2045where
2046 I: JavaStrSliceIndex,
2047{
2048 type Output = JavaStr;
2049
2050 #[inline]
2051 fn index(&self, index: I) -> &Self::Output {
2052 index.index(self)
2053 }
2054}
2055
2056impl<I> IndexMut<I> for JavaStr
2057where
2058 I: JavaStrSliceIndex,
2059{
2060 #[inline]
2061 fn index_mut(&mut self, index: I) -> &mut Self::Output {
2062 index.index_mut(self)
2063 }
2064}
2065
2066impl<'b> PartialEq<&'b JavaStr> for Cow<'_, str> {
2067 #[inline]
2068 fn eq(&self, other: &&'b JavaStr) -> bool {
2069 self == *other
2070 }
2071}
2072
2073impl<'b> PartialEq<&'b JavaStr> for Cow<'_, JavaStr> {
2074 #[inline]
2075 fn eq(&self, other: &&'b JavaStr) -> bool {
2076 self == *other
2077 }
2078}
2079
2080impl<'a> PartialEq<Cow<'a, str>> for &JavaStr {
2081 #[inline]
2082 fn eq(&self, other: &Cow<'a, str>) -> bool {
2083 *self == other
2084 }
2085}
2086
2087impl<'a> PartialEq<Cow<'a, str>> for JavaStr {
2088 #[inline]
2089 fn eq(&self, other: &Cow<'a, str>) -> bool {
2090 other == self
2091 }
2092}
2093
2094impl<'a> PartialEq<Cow<'a, JavaStr>> for &JavaStr {
2095 #[inline]
2096 fn eq(&self, other: &Cow<'a, JavaStr>) -> bool {
2097 *self == other
2098 }
2099}
2100
2101impl<'a> PartialEq<Cow<'a, JavaStr>> for JavaStr {
2102 #[inline]
2103 fn eq(&self, other: &Cow<'a, JavaStr>) -> bool {
2104 other == self
2105 }
2106}
2107
2108impl PartialEq<String> for &JavaStr {
2109 #[inline]
2110 fn eq(&self, other: &String) -> bool {
2111 *self == other
2112 }
2113}
2114
2115impl PartialEq<String> for JavaStr {
2116 #[inline]
2117 fn eq(&self, other: &String) -> bool {
2118 self == &other[..]
2119 }
2120}
2121
2122impl PartialEq<JavaStr> for String {
2123 #[inline]
2124 fn eq(&self, other: &JavaStr) -> bool {
2125 &self[..] == other
2126 }
2127}
2128
2129impl PartialEq<JavaString> for &JavaStr {
2130 #[inline]
2131 fn eq(&self, other: &JavaString) -> bool {
2132 *self == other
2133 }
2134}
2135
2136impl PartialEq<JavaString> for JavaStr {
2137 #[inline]
2138 fn eq(&self, other: &JavaString) -> bool {
2139 self == other[..]
2140 }
2141}
2142
2143impl PartialEq<JavaStr> for Cow<'_, str> {
2144 #[inline]
2145 fn eq(&self, other: &JavaStr) -> bool {
2146 match self {
2147 Cow::Borrowed(this) => this == other,
2148 Cow::Owned(this) => this == other,
2149 }
2150 }
2151}
2152
2153impl PartialEq<JavaStr> for Cow<'_, JavaStr> {
2154 #[inline]
2155 fn eq(&self, other: &JavaStr) -> bool {
2156 match self {
2157 Cow::Borrowed(this) => this == other,
2158 Cow::Owned(this) => this == other,
2159 }
2160 }
2161}
2162
2163impl PartialEq<JavaStr> for str {
2164 #[inline]
2165 fn eq(&self, other: &JavaStr) -> bool {
2166 JavaStr::from_str(self) == other
2167 }
2168}
2169
2170impl PartialEq<JavaStr> for &str {
2171 #[inline]
2172 fn eq(&self, other: &JavaStr) -> bool {
2173 self.as_bytes() == &other.inner
2174 }
2175}
2176
2177impl PartialEq<str> for JavaStr {
2178 #[inline]
2179 fn eq(&self, other: &str) -> bool {
2180 &self.inner == other.as_bytes()
2181 }
2182}
2183
2184impl<'a> PartialEq<&'a str> for JavaStr {
2185 #[inline]
2186 fn eq(&self, other: &&'a str) -> bool {
2187 &self.inner == other.as_bytes()
2188 }
2189}
2190
2191impl PartialEq<JavaStr> for &JavaStr {
2192 #[inline]
2193 fn eq(&self, other: &JavaStr) -> bool {
2194 self.inner == other.inner
2195 }
2196}
2197
2198impl<'a> PartialEq<&'a JavaStr> for JavaStr {
2199 #[inline]
2200 fn eq(&self, other: &&'a JavaStr) -> bool {
2201 self.inner == other.inner
2202 }
2203}
2204
2205impl ToOwned for JavaStr {
2206 type Owned = JavaString;
2207
2208 #[inline]
2209 fn to_owned(&self) -> Self::Owned {
2210 unsafe { JavaString::from_semi_utf8_unchecked(self.as_bytes().to_vec()) }
2211 }
2212}
2213
2214mod private_slice_index {
2215 use std::ops;
2216
2217 pub trait Sealed {}
2218
2219 impl Sealed for ops::Range<usize> {}
2220 impl Sealed for ops::RangeTo<usize> {}
2221 impl Sealed for ops::RangeFrom<usize> {}
2222 impl Sealed for ops::RangeFull {}
2223 impl Sealed for ops::RangeInclusive<usize> {}
2224 impl Sealed for ops::RangeToInclusive<usize> {}
2225}
2226
2227/// # Safety
2228///
2229/// Implementations' `check_bounds` method must properly check the bounds of the
2230/// slice, such that calling `get_unchecked` is not UB.
2231pub unsafe trait JavaStrSliceIndex: private_slice_index::Sealed + Sized {
2232 fn check_bounds(&self, slice: &JavaStr) -> bool;
2233 fn check_bounds_fail(self, slice: &JavaStr) -> !;
2234
2235 /// # Safety
2236 ///
2237 /// - The input slice must be a valid pointer
2238 /// - This index must not be out of bounds of the input slice
2239 /// - The indices of this slice must point to char boundaries in the input
2240 /// slice
2241 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr;
2242
2243 /// # Safety
2244 ///
2245 /// - The input slice must be a valid pointer
2246 /// - This index must not be out of bounds of the input slice
2247 /// - The indices of this slice must point to char boundaries in the input
2248 /// slice
2249 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr;
2250
2251 #[inline]
2252 fn get(self, slice: &JavaStr) -> Option<&JavaStr> {
2253 self.check_bounds(slice)
2254 .then(|| unsafe { &*self.get_unchecked(slice) })
2255 }
2256
2257 #[inline]
2258 fn get_mut(self, slice: &mut JavaStr) -> Option<&mut JavaStr> {
2259 self.check_bounds(slice)
2260 .then(|| unsafe { &mut *self.get_unchecked_mut(slice) })
2261 }
2262
2263 #[inline]
2264 fn index(self, slice: &JavaStr) -> &JavaStr {
2265 if self.check_bounds(slice) {
2266 unsafe { &*self.get_unchecked(slice) }
2267 } else {
2268 self.check_bounds_fail(slice)
2269 }
2270 }
2271
2272 #[inline]
2273 fn index_mut(self, slice: &mut JavaStr) -> &mut JavaStr {
2274 if self.check_bounds(slice) {
2275 unsafe { &mut *self.get_unchecked_mut(slice) }
2276 } else {
2277 self.check_bounds_fail(slice)
2278 }
2279 }
2280}
2281
2282unsafe impl JavaStrSliceIndex for RangeFull {
2283 #[inline]
2284 fn check_bounds(&self, _slice: &JavaStr) -> bool {
2285 true
2286 }
2287
2288 #[inline]
2289 fn check_bounds_fail(self, _slice: &JavaStr) -> ! {
2290 unreachable!()
2291 }
2292
2293 #[inline]
2294 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2295 slice
2296 }
2297
2298 #[inline]
2299 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2300 slice
2301 }
2302}
2303
2304unsafe impl JavaStrSliceIndex for Range<usize> {
2305 #[inline]
2306 fn check_bounds(&self, slice: &JavaStr) -> bool {
2307 self.start <= self.end
2308 && slice.is_char_boundary(self.start)
2309 && slice.is_char_boundary(self.end)
2310 }
2311
2312 #[inline]
2313 #[track_caller]
2314 fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2315 slice_error_fail(slice, self.start, self.end)
2316 }
2317
2318 #[inline]
2319 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2320 let slice = slice as *const [u8];
2321 // SAFETY: the caller guarantees that `self` is in bounds of `slice`
2322 // which satisfies all the conditions for `add`.
2323 let ptr = unsafe { (slice as *const u8).add(self.start) };
2324 let len = self.end - self.start;
2325 ptr::slice_from_raw_parts(ptr, len) as *const JavaStr
2326 }
2327
2328 #[inline]
2329 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2330 let slice = slice as *mut [u8];
2331 // SAFETY: see comments for `get_unchecked`.
2332 let ptr = unsafe { (slice as *mut u8).add(self.start) };
2333 let len = self.end - self.start;
2334 ptr::slice_from_raw_parts_mut(ptr, len) as *mut JavaStr
2335 }
2336}
2337
2338unsafe impl JavaStrSliceIndex for RangeTo<usize> {
2339 #[inline]
2340 fn check_bounds(&self, slice: &JavaStr) -> bool {
2341 slice.is_char_boundary(self.end)
2342 }
2343
2344 #[inline]
2345 #[track_caller]
2346 fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2347 slice_error_fail(slice, 0, self.end)
2348 }
2349
2350 #[inline]
2351 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2352 unsafe { (0..self.end).get_unchecked(slice) }
2353 }
2354
2355 #[inline]
2356 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2357 unsafe { (0..self.end).get_unchecked_mut(slice) }
2358 }
2359}
2360
2361unsafe impl JavaStrSliceIndex for RangeFrom<usize> {
2362 #[inline]
2363 fn check_bounds(&self, slice: &JavaStr) -> bool {
2364 slice.is_char_boundary(self.start)
2365 }
2366
2367 #[inline]
2368 #[track_caller]
2369 fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2370 slice_error_fail(slice, self.start, slice.len())
2371 }
2372
2373 #[inline]
2374 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2375 #[allow(clippy::needless_borrow)]
2376 let len = unsafe { (&(*(slice as *const [u8]))).len() };
2377 unsafe { (self.start..len).get_unchecked(slice) }
2378 }
2379
2380 #[inline]
2381 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2382 #[allow(clippy::needless_borrow)]
2383 let len = unsafe { (&(*(slice as *mut [u8]))).len() };
2384 unsafe { (self.start..len).get_unchecked_mut(slice) }
2385 }
2386}
2387
2388#[inline]
2389fn into_slice_range(range: RangeInclusive<usize>) -> Range<usize> {
2390 let exclusive_end = *range.end() + 1;
2391 let start = match range.end_bound() {
2392 Bound::Excluded(..) => exclusive_end, // excluded
2393 Bound::Included(..) => *range.start(),
2394 Bound::Unbounded => unreachable!(),
2395 };
2396 start..exclusive_end
2397}
2398
2399unsafe impl JavaStrSliceIndex for RangeInclusive<usize> {
2400 #[inline]
2401 fn check_bounds(&self, slice: &JavaStr) -> bool {
2402 *self.end() != usize::MAX && into_slice_range(self.clone()).check_bounds(slice)
2403 }
2404
2405 #[inline]
2406 #[track_caller]
2407 fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2408 if *self.end() == usize::MAX {
2409 str_end_index_overflow_fail()
2410 } else {
2411 into_slice_range(self).check_bounds_fail(slice)
2412 }
2413 }
2414
2415 #[inline]
2416 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2417 into_slice_range(self).get_unchecked(slice)
2418 }
2419
2420 #[inline]
2421 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2422 into_slice_range(self).get_unchecked_mut(slice)
2423 }
2424}
2425
2426unsafe impl JavaStrSliceIndex for RangeToInclusive<usize> {
2427 #[inline]
2428 fn check_bounds(&self, slice: &JavaStr) -> bool {
2429 (0..=self.end).check_bounds(slice)
2430 }
2431
2432 #[inline]
2433 fn check_bounds_fail(self, slice: &JavaStr) -> ! {
2434 (0..=self.end).check_bounds_fail(slice)
2435 }
2436
2437 #[inline]
2438 unsafe fn get_unchecked(self, slice: *const JavaStr) -> *const JavaStr {
2439 (0..=self.end).get_unchecked(slice)
2440 }
2441
2442 #[inline]
2443 unsafe fn get_unchecked_mut(self, slice: *mut JavaStr) -> *mut JavaStr {
2444 (0..=self.end).get_unchecked_mut(slice)
2445 }
2446}