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