Skip to content

Commit dedea7b

Browse files
mina86pitaj
andcommitted
core: introduce internal core::pattern::{Split,SplitN} types
Introduce core::pattern::Split and core::pattern::SplitN internal types which can be used to implement iterators splitting haystack into parts. Convert str’s Split-family of iterators to use them. In the future, more haystsacks will use those internal types. Co-authored-by: Peter Jaszkowiak <p.jaszkow@gmail.com>
1 parent 9ba42ae commit dedea7b

File tree

3 files changed

+281
-189
lines changed

3 files changed

+281
-189
lines changed

library/core/src/pattern.rs

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
issue = "27721"
3737
)]
3838

39+
use crate::fmt;
40+
use crate::mem::replace;
3941
use crate::ops::Range;
4042

4143
/// A pattern which can be matched against a [`Haystack`].
@@ -360,3 +362,243 @@ pub unsafe trait ReverseSearcher<H: Haystack>: Searcher<H> {
360362
/// in the haystack `"aaa"` matches as either `"[aa]a"` or `"a[aa]"`, depending
361363
/// from which side it is searched.
362364
pub trait DoubleEndedSearcher<H: Haystack>: ReverseSearcher<H> {}
365+
366+
//////////////////////////////////////////////////////////////////////////////
367+
// Internal Split and SplitN implementations
368+
//////////////////////////////////////////////////////////////////////////////
369+
370+
/// Helper type for implementing split iterators.
371+
///
372+
/// It’s a generic type which works with any [`Haystack`] and [`Searcher`] over
373+
/// that haystack. Intended usage is to create a newtype wrapping this type
374+
/// which implements iterator interface on top of [`next_fwd`][Split::next_fwd]
375+
/// or [`next_fwd`][Split::next_fwd] methods.
376+
///
377+
/// Note that unless `S` implements [`DoubleEndedSearcher`] trait, it’s
378+
/// incorrect to use this type to implement a double ended iterator.
379+
///
380+
/// For an example of this type in use, see [`core::str::Split`].
381+
#[unstable(feature = "pattern_internals", issue = "none")]
382+
pub struct Split<H: Haystack, S: Searcher<H>> {
383+
/// Start of the region of the haystack yet to be examined.
384+
start: H::Cursor,
385+
/// End of the region of the haystack yet to be examined.
386+
end: H::Cursor,
387+
/// Searcher returning matches of the delimiter pattern.
388+
searcher: S,
389+
/// Whether to return an empty part if there’s delimiter at the end of the
390+
/// haystack.
391+
allow_trailing_empty: bool,
392+
/// Whether splitting has finished.
393+
finished: bool,
394+
}
395+
396+
/// Helper type for implementing split iterators with a split limit.
397+
///
398+
/// It’s like [`Split`] but limits number of parts the haystack will be split
399+
/// into.
400+
#[unstable(feature = "pattern_internals", issue = "none")]
401+
pub struct SplitN<H: Haystack, S: Searcher<H>> {
402+
/// Inner split implementation.
403+
inner: Split<H, S>,
404+
/// Maximum number of parts the haystack can be split into.
405+
limit: usize,
406+
}
407+
408+
impl<H: Haystack, S: Searcher<H> + Clone> Clone for Split<H, S> {
409+
fn clone(&self) -> Self {
410+
Self { searcher: self.searcher.clone(), ..*self }
411+
}
412+
}
413+
414+
impl<H: Haystack, S: Searcher<H> + Clone> Clone for SplitN<H, S> {
415+
fn clone(&self) -> Self {
416+
Self { inner: self.inner.clone(), ..*self }
417+
}
418+
}
419+
420+
impl<H, S> fmt::Debug for Split<H, S>
421+
where
422+
H: Haystack<Cursor: fmt::Debug>,
423+
S: Searcher<H> + fmt::Debug,
424+
{
425+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
426+
fmt.debug_struct("Split")
427+
.field("start", &self.start)
428+
.field("end", &self.end)
429+
.field("searcher", &self.searcher)
430+
.field("allow_trailing_empty", &self.allow_trailing_empty)
431+
.field("finished", &self.finished)
432+
.finish()
433+
}
434+
}
435+
436+
impl<H, S> fmt::Debug for SplitN<H, S>
437+
where
438+
H: Haystack<Cursor: fmt::Debug>,
439+
S: Searcher<H> + fmt::Debug,
440+
{
441+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
442+
fmt.debug_struct("SplitN").field("inner", &self.inner).field("limit", &self.limit).finish()
443+
}
444+
}
445+
446+
impl<H: Haystack, S: Searcher<H>> Split<H, S> {
447+
/// Creates a new object configured without a limit and with
448+
/// `allow_trailing_empty` option disabled.
449+
///
450+
/// To set `allow_trailing_empty`, use
451+
/// [`with_allow_trailing_empty()`][Self::with_allow_trailing_empty] method.
452+
/// To set split limit, use [`with_limit()`][Self::with_limit] method.
453+
pub fn new(searcher: S) -> Self {
454+
let haystack = searcher.haystack();
455+
Self {
456+
searcher,
457+
start: haystack.cursor_at_front(),
458+
end: haystack.cursor_at_back(),
459+
allow_trailing_empty: false,
460+
finished: false,
461+
}
462+
}
463+
464+
/// Changes splits limit from unlimited to given value.
465+
///
466+
/// The limit specifies maximum number of parts haystack will be split into.
467+
pub fn with_limit(self, limit: usize) -> SplitN<H, S> {
468+
SplitN { inner: self, limit }
469+
}
470+
471+
/// Enables allow_trailing_empty option.
472+
///
473+
/// If enabled (which is not the default), if the haystack is empty or
474+
/// terminated by a pattern match, the last haystack part returned will be
475+
/// empty. Otherwise, the last empty split is not returned.
476+
pub fn with_allow_trailing_empty(mut self) -> Self {
477+
self.allow_trailing_empty = true;
478+
self
479+
}
480+
}
481+
482+
impl<H: Haystack, S: Searcher<H>> Split<H, S> {
483+
/// Returns next part of the haystack or `None` if splitting is done.
484+
///
485+
/// If `INCLUSIVE` is `true`, returned value will include the matching
486+
/// pattern.
487+
pub fn next_fwd<const INCLUSIVE: bool>(&mut self) -> Option<H> {
488+
if self.finished {
489+
return None;
490+
}
491+
let haystack = self.searcher.haystack();
492+
if let Some((start, end)) = self.searcher.next_match() {
493+
let range = self.start..(if INCLUSIVE { end } else { start });
494+
self.start = end;
495+
// SAFETY: self.start and self.end come from Haystack or Searcher
496+
// and thus are guaranteed to be valid split positions.
497+
Some(unsafe { haystack.get_unchecked(range) })
498+
} else {
499+
self.get_end()
500+
}
501+
}
502+
503+
/// Returns next looking from back of the haystack part of the haystack or
504+
/// `None` if splitting is done.
505+
///
506+
/// If `INCLUSIVE` is `true`, returned value will include the matching
507+
/// pattern.
508+
pub fn next_bwd<const INCLUSIVE: bool>(&mut self) -> Option<H>
509+
where
510+
S: ReverseSearcher<H>,
511+
{
512+
if self.finished {
513+
return None;
514+
}
515+
516+
if !self.allow_trailing_empty {
517+
self.allow_trailing_empty = true;
518+
if let Some(elt) = self.next_bwd::<INCLUSIVE>() {
519+
if !elt.is_empty() {
520+
return Some(elt);
521+
}
522+
}
523+
if self.finished {
524+
return None;
525+
}
526+
}
527+
528+
let range = if let Some((start, end)) = self.searcher.next_match_back() {
529+
end..replace(&mut self.end, if INCLUSIVE { end } else { start })
530+
} else {
531+
self.finished = true;
532+
self.start..self.end
533+
};
534+
// SAFETY: All indices come from Haystack or Searcher which guarantee
535+
// that they are valid split positions.
536+
Some(unsafe { self.searcher.haystack().get_unchecked(range) })
537+
}
538+
539+
/// Returns remaining part of the haystack that hasn’t been processed yet.
540+
pub fn remainder(&self) -> Option<H> {
541+
(!self.finished).then(|| {
542+
// SAFETY: self.start and self.end come from Haystack or Searcher
543+
// and thus are guaranteed to be valid split positions.
544+
unsafe { self.searcher.haystack().get_unchecked(self.start..self.end) }
545+
})
546+
}
547+
548+
/// Returns the final haystack part.
549+
///
550+
/// Sets `finished` flag so any further calls to this or other methods will
551+
/// return `None`.
552+
fn get_end(&mut self) -> Option<H> {
553+
if !self.finished {
554+
self.finished = true;
555+
if self.allow_trailing_empty || self.start != self.end {
556+
// SAFETY: self.start and self.end come from Haystack or
557+
// Searcher and thus are guaranteed to be valid split positions.
558+
return Some(unsafe {
559+
self.searcher.haystack().get_unchecked(self.start..self.end)
560+
});
561+
}
562+
}
563+
None
564+
}
565+
}
566+
567+
impl<H: Haystack, S: Searcher<H>> SplitN<H, S> {
568+
/// Returns next part of the haystack or `None` if splitting is done.
569+
///
570+
/// If `INCLUSIVE` is `true`, returned value will include the matching
571+
/// pattern.
572+
pub fn next_fwd<const INCLUSIVE: bool>(&mut self) -> Option<H> {
573+
match self.dec_limit()? {
574+
0 => self.inner.get_end(),
575+
_ => self.inner.next_fwd::<INCLUSIVE>(),
576+
}
577+
}
578+
579+
/// Returns next looking from back of the haystack part of the haystack or
580+
/// `None` if splitting is done.
581+
///
582+
/// If `INCLUSIVE` is `true`, returned value will include the matching
583+
/// pattern.
584+
pub fn next_bwd<const INCLUSIVE: bool>(&mut self) -> Option<H>
585+
where
586+
S: ReverseSearcher<H>,
587+
{
588+
match self.dec_limit()? {
589+
0 => self.inner.get_end(),
590+
_ => self.inner.next_bwd::<INCLUSIVE>(),
591+
}
592+
}
593+
594+
/// Returns remaining part of the haystack that hasn’t been processed yet.
595+
pub fn remainder(&self) -> Option<H> {
596+
self.inner.remainder()
597+
}
598+
599+
/// Decrements limit and returns its new value or None if it’s already zero.
600+
fn dec_limit(&mut self) -> Option<usize> {
601+
self.limit = self.limit.checked_sub(1)?;
602+
Some(self.limit)
603+
}
604+
}

0 commit comments

Comments
 (0)