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