Skip to content

Commit d01dc93

Browse files
committed
core: add core::pattern::EmptyNeedleSearcher internal type
Introduce core::pattern::EmptyNeedleSearcher internal type which implements logic for matching an empty pattern against a haystack. Convert core::str::pattern::StrSearcher to use it. In future more implementations will take advantage of it. Also adapt and rework TwoWayStrategy into an internal SearchResult trait which abstracts differences between Searcher’s next, next_match and next_rejects methods. It makes it simpler to write a single generic method implementing optimised versions of all those calls.
1 parent dedea7b commit d01dc93

File tree

2 files changed

+294
-177
lines changed

2 files changed

+294
-177
lines changed

library/core/src/pattern.rs

Lines changed: 214 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
)]
3838

3939
use crate::fmt;
40-
use crate::mem::replace;
40+
use crate::mem::{replace, take};
4141
use crate::ops::Range;
4242

4343
/// A pattern which can be matched against a [`Haystack`].
@@ -201,6 +201,84 @@ pub enum SearchStep<T = usize> {
201201
Done,
202202
}
203203

204+
/// Possible return type of a search.
205+
///
206+
/// It abstract differences between `next`, `next_match` and `next_reject`
207+
/// methods. Depending on return type an implementation for those functions
208+
/// will generate matches and rejects, only matches or only rejects.
209+
#[unstable(feature = "pattern_internals", issue = "none")]
210+
pub trait SearchResult<T = usize>: Sized + sealed::Sealed {
211+
/// Value indicating searching has finished.
212+
const DONE: Self;
213+
214+
/// Returns value describing a match or `None` if this implementation
215+
/// doesn’t care about matches.
216+
fn matching(start: T, end: T) -> Option<Self>;
217+
218+
/// Returns value describing a reject or `None` if this implementation
219+
/// doesn’t care about matches.
220+
fn rejecting(start: T, end: T) -> Option<Self>;
221+
}
222+
223+
/// A wrapper for result type which only carries information about matches.
224+
#[unstable(feature = "pattern_internals", issue = "none")]
225+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
226+
pub struct MatchOnly<T = usize>(pub Option<(T, T)>);
227+
228+
/// A wrapper for result type which only carries information about rejects.
229+
#[unstable(feature = "pattern_internals", issue = "none")]
230+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
231+
pub struct RejectOnly<T = usize>(pub Option<(T, T)>);
232+
233+
impl<T> SearchResult<T> for SearchStep<T> {
234+
const DONE: Self = SearchStep::Done;
235+
236+
#[inline(always)]
237+
fn matching(s: T, e: T) -> Option<Self> {
238+
Some(SearchStep::Match(s, e))
239+
}
240+
241+
#[inline(always)]
242+
fn rejecting(s: T, e: T) -> Option<Self> {
243+
Some(SearchStep::Reject(s, e))
244+
}
245+
}
246+
247+
impl<T> SearchResult<T> for MatchOnly<T> {
248+
const DONE: Self = Self(None);
249+
250+
#[inline(always)]
251+
fn matching(s: T, e: T) -> Option<Self> {
252+
Some(Self(Some((s, e))))
253+
}
254+
255+
#[inline(always)]
256+
fn rejecting(_s: T, _e: T) -> Option<Self> {
257+
None
258+
}
259+
}
260+
261+
impl<T> SearchResult<T> for RejectOnly<T> {
262+
const DONE: Self = Self(None);
263+
264+
#[inline(always)]
265+
fn matching(_s: T, _e: T) -> Option<Self> {
266+
None
267+
}
268+
269+
#[inline(always)]
270+
fn rejecting(s: T, e: T) -> Option<Self> {
271+
Some(Self(Some((s, e))))
272+
}
273+
}
274+
275+
mod sealed {
276+
pub trait Sealed {}
277+
impl<T> Sealed for super::SearchStep<T> {}
278+
impl<T> Sealed for super::MatchOnly<T> {}
279+
impl<T> Sealed for super::RejectOnly<T> {}
280+
}
281+
204282
/// A searcher for a string pattern.
205283
///
206284
/// This trait provides methods for searching for non-overlapping matches of
@@ -363,6 +441,141 @@ pub unsafe trait ReverseSearcher<H: Haystack>: Searcher<H> {
363441
/// from which side it is searched.
364442
pub trait DoubleEndedSearcher<H: Haystack>: ReverseSearcher<H> {}
365443

444+
//////////////////////////////////////////////////////////////////////////////
445+
// Internal EmptyNeedleSearcher helper
446+
//////////////////////////////////////////////////////////////////////////////
447+
448+
/// Helper for implementing searchers looking for empty patterns.
449+
///
450+
/// An empty pattern matches around every element of a haystack. For example,
451+
/// within a `&str` it matches around every character. (This includes at the
452+
/// beginning and end of the string).
453+
///
454+
/// This struct helps implement searchers for empty patterns for various
455+
/// haystacks. The only requirement is a function which advances the start
456+
/// position or end position of the haystack range.
457+
///
458+
/// # Examples
459+
///
460+
/// ```
461+
/// #![feature(pattern, pattern_internals)]
462+
/// use core::pattern::{EmptyNeedleSearcher, SearchStep};
463+
///
464+
/// let haystack = "fóó";
465+
/// let mut searcher = EmptyNeedleSearcher::new(haystack);
466+
/// let advance = |range: core::ops::Range<usize>| {
467+
/// range.start + haystack[range].chars().next().unwrap().len_utf8()
468+
/// };
469+
/// let steps = core::iter::from_fn(|| {
470+
/// match searcher.next_fwd(advance) {
471+
/// SearchStep::Done => None,
472+
/// step => Some(step)
473+
/// }
474+
/// }).collect::<Vec<_>>();
475+
/// assert_eq!(&[
476+
/// SearchStep::Match(0, 0),
477+
/// SearchStep::Reject(0, 1),
478+
/// SearchStep::Match(1, 1),
479+
/// SearchStep::Reject(1, 3),
480+
/// SearchStep::Match(3, 3),
481+
/// SearchStep::Reject(3, 5),
482+
/// SearchStep::Match(5, 5),
483+
/// ], steps.as_slice());
484+
/// ```
485+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
486+
#[unstable(feature = "pattern_internals", issue = "none")]
487+
pub struct EmptyNeedleSearcher<T> {
488+
start: T,
489+
end: T,
490+
is_match_fwd: bool,
491+
is_match_bwd: bool,
492+
// Needed in case of an empty haystack, see #85462
493+
is_finished: bool,
494+
}
495+
496+
impl<T: Copy + PartialOrd> EmptyNeedleSearcher<T> {
497+
/// Creates a new empty needle searcher for given haystack.
498+
///
499+
/// The haystack is used to initialise the range of valid cursors positions.
500+
pub fn new<H: Haystack<Cursor = T>>(haystack: H) -> Self {
501+
Self {
502+
start: haystack.cursor_at_front(),
503+
end: haystack.cursor_at_back(),
504+
is_match_bwd: true,
505+
is_match_fwd: true,
506+
is_finished: false,
507+
}
508+
}
509+
510+
/// Returns next search result.
511+
///
512+
/// The callback function is used to advance the **start** of the range the
513+
/// searcher is working on. It is passed the current range of cursor
514+
/// positions that weren’t visited yet and it must return the new start
515+
/// cursor position. It’s never called with an empty range. For some
516+
/// haystacks the callback may be as simple as a closure returning the start
517+
/// incremented by one; others might require looking for a new valid
518+
/// boundary.
519+
pub fn next_fwd<R: SearchResult<T>, F>(&mut self, advance_fwd: F) -> R
520+
where
521+
F: FnOnce(crate::ops::Range<T>) -> T,
522+
{
523+
if self.is_finished {
524+
return R::DONE;
525+
}
526+
if take(&mut self.is_match_fwd) {
527+
if let Some(ret) = R::matching(self.start, self.start) {
528+
return ret;
529+
}
530+
}
531+
if self.start < self.end {
532+
let pos = self.start;
533+
self.start = advance_fwd(self.start..self.end);
534+
if let Some(ret) = R::rejecting(pos, self.start) {
535+
self.is_match_fwd = true;
536+
return ret;
537+
}
538+
return R::matching(self.start, self.start).unwrap();
539+
}
540+
self.is_finished = true;
541+
R::DONE
542+
}
543+
544+
/// Returns next search result.
545+
///
546+
/// The callback function is used to advance the **end** of the range the
547+
/// searcher is working on backwards. It is passed the current range of
548+
/// cursor positions that weren’t visited yet and it must return the new end
549+
/// cursor position. It’s never called with an empty range. For some
550+
/// haystacks the callback may be as simple as a closure returning the end
551+
/// decremented by one; others might require looking for a new valid
552+
/// boundary.
553+
pub fn next_bwd<R: SearchResult<T>, F>(&mut self, advance_bwd: F) -> R
554+
where
555+
F: FnOnce(crate::ops::Range<T>) -> T,
556+
{
557+
if self.is_finished {
558+
return R::DONE;
559+
}
560+
if take(&mut self.is_match_bwd) {
561+
if let Some(ret) = R::matching(self.end, self.end) {
562+
return ret;
563+
}
564+
}
565+
if self.start < self.end {
566+
let pos = self.end;
567+
self.end = advance_bwd(self.start..self.end);
568+
if let Some(ret) = R::rejecting(self.end, pos) {
569+
self.is_match_bwd = true;
570+
return ret;
571+
}
572+
return R::matching(self.end, self.end).unwrap();
573+
}
574+
self.is_finished = true;
575+
R::DONE
576+
}
577+
}
578+
366579
//////////////////////////////////////////////////////////////////////////////
367580
// Internal Split and SplitN implementations
368581
//////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)