Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8331f04
Revert "Add ByteNeedle search in StrSearcherImpl"
pacak Aug 17, 2026
9a2ad87
sys: reduce visibility of some internal OsStr related types
pacak Aug 3, 2026
0281118
core: convert Pattern<'a> into Pattern<H: Haystack>
pacak Aug 3, 2026
43645b5
core: move Pattern et al to core::pattern module
pacak Aug 3, 2026
eba6588
core: introduce internal core::pattern::{Split,SplitN} types
pacak Aug 3, 2026
fe7c257
core: add core::pattern::EmptyNeedleSearcher internal type
pacak Aug 3, 2026
b3ea19c
core: add try_next_code_point{,_reverse} internal functions
pacak Aug 3, 2026
d28e413
core: refactor tests/pattern.rs tests
pacak Aug 3, 2026
e6bf894
coretests: Add some pattern tests.
pacak Aug 10, 2026
78b1870
core: add internal core::str_bytes module handling string-like slices
pacak Aug 3, 2026
abafe0d
coretests: Add a few tests for backward multibyte predicate
pacak Aug 11, 2026
6ee41dc
core: add concept of Flavour to core::str_bytes
pacak Aug 10, 2026
2e22ec1
std: add pattern matching to OsStr
pacak Aug 3, 2026
ba34860
core: add core::pattern::Predicate wrapper type
pacak Aug 3, 2026
345652c
std: add predicate pattern support to OsStr
pacak Aug 3, 2026
2895c6c
std: add OsStr pattern benchmarks
pacak Aug 17, 2026
44380bc
core/std: inline small functions
pacak Aug 17, 2026
2a3b755
core::str_bytes: use unsafe for indexing
pacak Aug 17, 2026
c46cd4c
core: skip emitting rejects if result doesn't cary it
pacak Aug 17, 2026
2537437
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