Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d1ed1f3
Revert "Add ByteNeedle search in StrSearcherImpl"
pacak Aug 17, 2026
0a0e970
sys: reduce visibility of some internal OsStr related types
pacak Aug 3, 2026
aaf30f7
core: convert Pattern<'a> into Pattern<H: Haystack>
pacak Aug 3, 2026
85d99ae
core: move Pattern et al to core::pattern module
pacak Aug 3, 2026
83e7a20
core: introduce internal core::pattern::{Split,SplitN} types
pacak Aug 3, 2026
842a2e9
core: add core::pattern::EmptyNeedleSearcher internal type
pacak Aug 3, 2026
926cb67
core: add try_next_code_point{,_reverse} internal functions
pacak Aug 3, 2026
d5f1d7a
core: refactor tests/pattern.rs tests
pacak Aug 3, 2026
fb3954f
coretests: Add some pattern tests.
pacak Aug 10, 2026
10f9008
core: add internal core::str_bytes module handling string-like slices
pacak Aug 3, 2026
84defbc
coretests: Add a few tests for backward multibyte predicate
pacak Aug 11, 2026
9cddb51
core: add concept of Flavour to core::str_bytes
pacak Aug 10, 2026
6901188
std: add pattern matching to OsStr
pacak Aug 3, 2026
417041e
core: add core::pattern::Predicate wrapper type
pacak Aug 3, 2026
a0b5561
std: add predicate pattern support to OsStr
pacak Aug 3, 2026
8579fd2
std: add find/rfind/contains to &OsStr
pacak Aug 19, 2026
982a843
core, std: support various [char] matchers for Pattern<&OsStr>
pacak Aug 20, 2026
a5b78ee
std: add #[inline] for all new OsStr methodd to match str
pacak Aug 19, 2026
f579860
std: add OsStr pattern benchmarks
pacak Aug 17, 2026
ee3bbc6
std: expand OsStr matching benchmarks
pacak Aug 20, 2026
0efacff
std: OsStr pattern benchmarks can actually use find/rfind now
pacak Aug 19, 2026
1a2ae91
core/std: inline small functions
pacak Aug 17, 2026
aca3976
core::str_bytes: use unsafe for indexing
pacak Aug 17, 2026
ec2d203
core: skip emitting rejects if result doesn't cary it
pacak Aug 17, 2026
9c672ee
core: add memchr-based single-ascii byte search to str_bytes
pacak Aug 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions library/alloc/src/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use core::borrow::{Borrow, BorrowMut};
use core::iter::FusedIterator;
use core::mem::MaybeUninit;
use core::pattern::{Pattern, Utf8Pattern};
#[stable(feature = "encode_utf16", since = "1.8.0")]
pub use core::str::EncodeUtf16;
#[stable(feature = "split_ascii_whitespace", since = "1.34.0")]
Expand All @@ -20,7 +21,6 @@ pub use core::str::SplitInclusive;
pub use core::str::SplitWhitespace;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::str::pattern;
use core::str::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher, Utf8Pattern};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::str::{Bytes, CharIndices, Chars, from_utf8, from_utf8_mut};
#[stable(feature = "str_escape", since = "1.34.0")]
Expand Down Expand Up @@ -305,7 +305,10 @@ impl str {
without modifying the original"]
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn replace<P: Pattern>(&self, from: P, to: &str) -> String {
pub fn replace<'a, P>(&'a self, from: P, to: &str) -> String
where
P: Pattern<&'a str>,
{
// Fast path for replacing a single ASCII character with another.
if let Some(from_byte) = match from.as_utf8_pattern() {
Some(Utf8Pattern::StringPattern(s)) => match s.as_bytes() {
Expand Down Expand Up @@ -363,7 +366,10 @@ impl str {
#[must_use = "this returns the replaced string as a new allocation, \
without modifying the original"]
#[stable(feature = "str_replacen", since = "1.16.0")]
pub fn replacen<P: Pattern>(&self, pat: P, to: &str, count: usize) -> String {
pub fn replacen<'a, P>(&'a self, pat: P, to: &str, count: usize) -> String
where
P: Pattern<&'a str>,
{
// Hope to reduce the times of re-allocation
let mut result = String::with_capacity(32);
let mut last_end = 0;
Expand Down
32 changes: 19 additions & 13 deletions library/alloc/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use core::ops::Add;
#[cfg(not(no_global_oom_handling))]
use core::ops::AddAssign;
use core::ops::{self, Range, RangeBounds};
use core::str::pattern::{Pattern, Utf8Pattern};
use core::pattern::{Pattern, Utf8Pattern};
use core::{fmt, hash, ptr, slice};

#[cfg(not(no_global_oom_handling))]
Expand Down Expand Up @@ -1580,8 +1580,11 @@ impl String {
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "string_remove_matches", issue = "72826")]
pub fn remove_matches<P: Pattern>(&mut self, pat: P) {
use core::str::pattern::Searcher;
pub fn remove_matches<'a, P>(&'a mut self, pat: P)
where
P: for<'x> Pattern<&'x str>,
{
use core::pattern::Searcher;

let rejections = {
let mut searcher = pat.into_searcher(self);
Expand Down Expand Up @@ -2130,7 +2133,10 @@ impl String {
/// [replacen]: ../../std/primitive.str.html#method.replacen
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "string_replace_in_place", issue = "147949")]
pub fn replace_first<P: Pattern>(&mut self, from: P, to: &str) {
pub fn replace_first<'a, P>(&'a mut self, from: P, to: &str)
where
P: for<'x> Pattern<&'x str>,
{
let range = match self.match_indices(from).next() {
Some((start, match_str)) => start..start + match_str.len(),
None => return,
Expand All @@ -2156,9 +2162,9 @@ impl String {
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "string_replace_in_place", issue = "147949")]
pub fn replace_last<P: Pattern>(&mut self, from: P, to: &str)
pub fn replace_last<'a, P>(&'a mut self, from: P, to: &str)
where
for<'a> P::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>,
P: for<'x> Pattern<&'x str, Searcher: core::pattern::ReverseSearcher<&'x str>>,
{
let range = match self.rmatch_indices(from).next() {
Some((start, match_str)) => start..start + match_str.len(),
Expand Down Expand Up @@ -2651,10 +2657,10 @@ impl<'a> Extend<&'a core::ascii::Char> for String {
reason = "API not fully fleshed out and ready to be stabilized",
issue = "27721"
)]
impl<'b> Pattern for &'b String {
type Searcher<'a> = <&'b str as Pattern>::Searcher<'a>;
impl<'a, 'b> Pattern<&'a str> for &'b String {
type Searcher = <&'b str as Pattern<&'a str>>::Searcher;

fn into_searcher(self, haystack: &str) -> <&'b str as Pattern>::Searcher<'_> {
fn into_searcher(self, haystack: &'a str) -> <&'b str as Pattern<&'a str>>::Searcher {
self[..].into_searcher(haystack)
}

Expand All @@ -2674,17 +2680,17 @@ impl<'b> Pattern for &'b String {
}

#[inline]
fn is_suffix_of<'a>(self, haystack: &'a str) -> bool
fn is_suffix_of(self, haystack: &'a str) -> bool
where
Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>,
Self::Searcher: core::pattern::ReverseSearcher<&'a str>,
{
self[..].is_suffix_of(haystack)
}

#[inline]
fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str>
fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str>
where
Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>,
Self::Searcher: core::pattern::ReverseSearcher<&'a str>,
{
self[..].strip_suffix_of(haystack)
}
Expand Down
62 changes: 37 additions & 25 deletions library/alloctests/tests/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,18 @@ fn test_trim_matches() {
assert_eq!("123foo1bar123".trim_matches(|c: char| c.is_numeric()), "foo1bar");
}

#[test]
fn test_trim_matches_with_str_pattern() {
assert_eq!("abc".trim_start_matches("ab"), "c");
assert_eq!("xyzabcxyz".trim_start_matches("xyz"), "abcxyz");
assert_eq!("abcabc".trim_start_matches("abc"), "");
assert_eq!("ababab".trim_start_matches("ab"), "");

assert_eq!("abcab".trim_end_matches("ab"), "abc");
assert_eq!("xyzabcxyz".trim_end_matches("xyz"), "xyzabc");
assert_eq!("abcabc".trim_end_matches("abc"), "");
}

#[test]
fn test_trim_start() {
assert_eq!("".trim_start(), "");
Expand Down Expand Up @@ -2009,14 +2021,14 @@ fn test_repeat() {
}

mod pattern {
use std::str::pattern::SearchStep::{self, Done, Match, Reject};
use std::str::pattern::{Pattern, ReverseSearcher, Searcher};
use std::pattern::SearchStep::{self, Done, Match, Reject};
use std::pattern::{Pattern, ReverseSearcher, Searcher};

macro_rules! make_test {
($name:ident, $p:expr, $h:expr, [$($e:expr,)*]) => {
#[allow(unused_imports)]
mod $name {
use std::str::pattern::SearchStep::{Match, Reject};
use std::pattern::SearchStep::{Match, Reject};
use super::{cmp_search_to_vec};
#[test]
fn fwd() {
Expand All @@ -2032,7 +2044,7 @@ mod pattern {

fn cmp_search_to_vec<P>(rev: bool, pat: P, haystack: &str, right: Vec<SearchStep>)
where
P: for<'a> Pattern<Searcher<'a>: ReverseSearcher<'a>>,
P: for<'a> Pattern<&'a str, Searcher: ReverseSearcher<&'a str>>,
{
let mut searcher = pat.into_searcher(haystack);
let mut v = vec![];
Expand Down Expand Up @@ -2107,12 +2119,7 @@ mod pattern {
Match(7, 7),
]
);
make_test!(
str_searcher_multibyte_haystack,
" ",
"├──",
[Reject(0, 3), Reject(3, 6), Reject(6, 9),]
);
make_test!(str_searcher_multibyte_haystack, " ", "├──", [Reject(0, 9),]);
make_test!(
str_searcher_empty_needle_multibyte_haystack,
"",
Expand Down Expand Up @@ -2143,18 +2150,8 @@ mod pattern {
Reject(6, 7),
]
);
make_test!(
char_searcher_multibyte_haystack,
' ',
"├──",
[Reject(0, 3), Reject(3, 6), Reject(6, 9),]
);
make_test!(
char_searcher_short_haystack,
'\u{1F4A9}',
"* \t",
[Reject(0, 1), Reject(1, 2), Reject(2, 3),]
);
make_test!(char_searcher_multibyte_haystack, ' ', "├──", [Reject(0, 9),]);
make_test!(char_searcher_short_haystack, '\u{1F4A9}', "* \t", [Reject(0, 3),]);

// See #85462
#[test]
Expand Down Expand Up @@ -2196,6 +2193,21 @@ mod pattern {
assert_eq!(searcher.next_back(), SearchStep::Done);
}
}

#[test]
fn str_searcher_empty_needle_interleaved() {
let mut searcher = "".into_searcher("abc");

assert_eq!(searcher.next(), SearchStep::Match(0, 0));
assert_eq!(searcher.next_back(), SearchStep::Match(3, 3));
assert_eq!(searcher.next(), SearchStep::Reject(0, 1));
assert_eq!(searcher.next_back(), SearchStep::Reject(2, 3));
assert_eq!(searcher.next(), SearchStep::Match(1, 1));
assert_eq!(searcher.next_back(), SearchStep::Match(2, 2));
assert_eq!(searcher.next(), SearchStep::Reject(1, 2));
assert_eq!(searcher.next_back(), SearchStep::Done);
assert_eq!(searcher.next(), SearchStep::Done);
}
}

macro_rules! generate_iterator_test {
Expand Down Expand Up @@ -2290,11 +2302,11 @@ generate_iterator_test! {

#[test]
fn different_str_pattern_forwarding_lifetimes() {
use std::str::pattern::Pattern;
use std::pattern::Pattern;

fn foo<P>(p: P)
fn foo<'a, P>(p: P)
where
for<'b> &'b P: Pattern,
for<'b> &'b P: Pattern<&'a str>,
{
for _ in 0..3 {
"asdf".find(&p);
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,10 @@ pub mod unsafe_binder;

pub mod fmt;
pub mod hash;
pub mod pattern;
pub mod slice;
pub mod str;
pub mod str_bytes;
pub mod time;

pub mod wtf8;
Expand Down
Loading
Loading