Coverage Report

Created: 2026-05-30 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/memchr-2.8.1/src/memmem/mod.rs
Line
Count
Source
1
/*!
2
This module provides forward and reverse substring search routines.
3
4
Unlike the standard library's substring search routines, these work on
5
arbitrary bytes. For all non-empty needles, these routines will report exactly
6
the same values as the corresponding routines in the standard library. For
7
the empty needle, the standard library reports matches only at valid UTF-8
8
boundaries, where as these routines will report matches at every position.
9
10
Other than being able to work on arbitrary bytes, the primary reason to prefer
11
these routines over the standard library routines is that these will generally
12
be faster. In some cases, significantly so.
13
14
# Example: iterating over substring matches
15
16
This example shows how to use [`find_iter`] to find occurrences of a substring
17
in a haystack.
18
19
```
20
use memchr::memmem;
21
22
let haystack = b"foo bar foo baz foo";
23
24
let mut it = memmem::find_iter(haystack, "foo");
25
assert_eq!(Some(0), it.next());
26
assert_eq!(Some(8), it.next());
27
assert_eq!(Some(16), it.next());
28
assert_eq!(None, it.next());
29
```
30
31
# Example: iterating over substring matches in reverse
32
33
This example shows how to use [`rfind_iter`] to find occurrences of a substring
34
in a haystack starting from the end of the haystack.
35
36
**NOTE:** This module does not implement double ended iterators, so reverse
37
searches aren't done by calling `rev` on a forward iterator.
38
39
```
40
use memchr::memmem;
41
42
let haystack = b"foo bar foo baz foo";
43
44
let mut it = memmem::rfind_iter(haystack, "foo");
45
assert_eq!(Some(16), it.next());
46
assert_eq!(Some(8), it.next());
47
assert_eq!(Some(0), it.next());
48
assert_eq!(None, it.next());
49
```
50
51
# Example: repeating a search for the same needle
52
53
It may be possible for the overhead of constructing a substring searcher to be
54
measurable in some workloads. In cases where the same needle is used to search
55
many haystacks, it is possible to do construction once and thus to avoid it for
56
subsequent searches. This can be done with a [`Finder`] (or a [`FinderRev`] for
57
reverse searches).
58
59
```
60
use memchr::memmem;
61
62
let finder = memmem::Finder::new("foo");
63
64
assert_eq!(Some(4), finder.find(b"baz foo quux"));
65
assert_eq!(None, finder.find(b"quux baz bar"));
66
```
67
*/
68
69
pub use crate::memmem::searcher::PrefilterConfig as Prefilter;
70
71
// This is exported here for use in the crate::arch::all::twoway
72
// implementation. This is essentially an abstraction breaker. Namely, the
73
// public API of twoway doesn't support providing a prefilter, but its crate
74
// internal API does. The main reason for this is that I didn't want to do the
75
// API design required to support it without a concrete use case.
76
pub(crate) use crate::memmem::searcher::Pre;
77
78
use crate::{
79
    arch::all::{
80
        packedpair::{DefaultFrequencyRank, HeuristicFrequencyRank},
81
        rabinkarp,
82
    },
83
    cow::CowBytes,
84
    memmem::searcher::{PrefilterState, Searcher, SearcherRev},
85
};
86
87
mod searcher;
88
89
/// Returns an iterator over all non-overlapping occurrences of a substring in
90
/// a haystack.
91
///
92
/// # Complexity
93
///
94
/// This routine is guaranteed to have worst case linear time complexity
95
/// with respect to both the needle and the haystack. That is, this runs
96
/// in `O(needle.len() + haystack.len())` time.
97
///
98
/// This routine is also guaranteed to have worst case constant space
99
/// complexity.
100
///
101
/// # Examples
102
///
103
/// Basic usage:
104
///
105
/// ```
106
/// use memchr::memmem;
107
///
108
/// let haystack = b"foo bar foo baz foo";
109
/// let mut it = memmem::find_iter(haystack, b"foo");
110
/// assert_eq!(Some(0), it.next());
111
/// assert_eq!(Some(8), it.next());
112
/// assert_eq!(Some(16), it.next());
113
/// assert_eq!(None, it.next());
114
/// ```
115
#[inline]
116
0
pub fn find_iter<'h, 'n, N: 'n + ?Sized + AsRef<[u8]>>(
117
0
    haystack: &'h [u8],
118
0
    needle: &'n N,
119
0
) -> FindIter<'h, 'n> {
120
0
    FindIter::new(haystack, Finder::new(needle))
121
0
}
122
123
/// Returns a reverse iterator over all non-overlapping occurrences of a
124
/// substring in a haystack.
125
///
126
/// # Complexity
127
///
128
/// This routine is guaranteed to have worst case linear time complexity
129
/// with respect to both the needle and the haystack. That is, this runs
130
/// in `O(needle.len() + haystack.len())` time.
131
///
132
/// This routine is also guaranteed to have worst case constant space
133
/// complexity.
134
///
135
/// # Examples
136
///
137
/// Basic usage:
138
///
139
/// ```
140
/// use memchr::memmem;
141
///
142
/// let haystack = b"foo bar foo baz foo";
143
/// let mut it = memmem::rfind_iter(haystack, b"foo");
144
/// assert_eq!(Some(16), it.next());
145
/// assert_eq!(Some(8), it.next());
146
/// assert_eq!(Some(0), it.next());
147
/// assert_eq!(None, it.next());
148
/// ```
149
#[inline]
150
0
pub fn rfind_iter<'h, 'n, N: 'n + ?Sized + AsRef<[u8]>>(
151
0
    haystack: &'h [u8],
152
0
    needle: &'n N,
153
0
) -> FindRevIter<'h, 'n> {
154
0
    FindRevIter::new(haystack, FinderRev::new(needle))
155
0
}
156
157
/// Returns the index of the first occurrence of the given needle.
158
///
159
/// Note that if you're are searching for the same needle in many different
160
/// small haystacks, it may be faster to initialize a [`Finder`] once,
161
/// and reuse it for each search.
162
///
163
/// # Complexity
164
///
165
/// This routine is guaranteed to have worst case linear time complexity
166
/// with respect to both the needle and the haystack. That is, this runs
167
/// in `O(needle.len() + haystack.len())` time.
168
///
169
/// This routine is also guaranteed to have worst case constant space
170
/// complexity.
171
///
172
/// # Examples
173
///
174
/// Basic usage:
175
///
176
/// ```
177
/// use memchr::memmem;
178
///
179
/// let haystack = b"foo bar baz";
180
/// assert_eq!(Some(0), memmem::find(haystack, b"foo"));
181
/// assert_eq!(Some(4), memmem::find(haystack, b"bar"));
182
/// assert_eq!(None, memmem::find(haystack, b"quux"));
183
/// ```
184
#[inline]
185
0
pub fn find(haystack: &[u8], needle: &[u8]) -> Option<usize> {
186
0
    if haystack.len() < 64 {
187
0
        rabinkarp::Finder::new(needle).find(haystack, needle)
188
    } else {
189
0
        Finder::new(needle).find(haystack)
190
    }
191
0
}
192
193
/// Returns the index of the last occurrence of the given needle.
194
///
195
/// Note that if you're are searching for the same needle in many different
196
/// small haystacks, it may be faster to initialize a [`FinderRev`] once,
197
/// and reuse it for each search.
198
///
199
/// # Complexity
200
///
201
/// This routine is guaranteed to have worst case linear time complexity
202
/// with respect to both the needle and the haystack. That is, this runs
203
/// in `O(needle.len() + haystack.len())` time.
204
///
205
/// This routine is also guaranteed to have worst case constant space
206
/// complexity.
207
///
208
/// # Examples
209
///
210
/// Basic usage:
211
///
212
/// ```
213
/// use memchr::memmem;
214
///
215
/// let haystack = b"foo bar baz";
216
/// assert_eq!(Some(0), memmem::rfind(haystack, b"foo"));
217
/// assert_eq!(Some(4), memmem::rfind(haystack, b"bar"));
218
/// assert_eq!(Some(8), memmem::rfind(haystack, b"ba"));
219
/// assert_eq!(None, memmem::rfind(haystack, b"quux"));
220
/// ```
221
#[inline]
222
0
pub fn rfind(haystack: &[u8], needle: &[u8]) -> Option<usize> {
223
0
    if haystack.len() < 64 {
224
0
        rabinkarp::FinderRev::new(needle).rfind(haystack, needle)
225
    } else {
226
0
        FinderRev::new(needle).rfind(haystack)
227
    }
228
0
}
229
230
/// An iterator over non-overlapping substring matches.
231
///
232
/// Matches are reported by the byte offset at which they begin.
233
///
234
/// `'h` is the lifetime of the haystack while `'n` is the lifetime of the
235
/// needle.
236
#[derive(Debug, Clone)]
237
pub struct FindIter<'h, 'n> {
238
    haystack: &'h [u8],
239
    prestate: PrefilterState,
240
    finder: Finder<'n>,
241
    pos: usize,
242
}
243
244
impl<'h, 'n> FindIter<'h, 'n> {
245
    #[inline(always)]
246
0
    pub(crate) fn new(
247
0
        haystack: &'h [u8],
248
0
        finder: Finder<'n>,
249
0
    ) -> FindIter<'h, 'n> {
250
0
        let prestate = PrefilterState::new();
251
0
        FindIter { haystack, prestate, finder, pos: 0 }
252
0
    }
253
254
    /// Convert this iterator into its owned variant, such that it no longer
255
    /// borrows the finder and needle.
256
    ///
257
    /// If this is already an owned iterator, then this is a no-op. Otherwise,
258
    /// this copies the needle.
259
    ///
260
    /// This is only available when the `alloc` feature is enabled.
261
    #[cfg(feature = "alloc")]
262
    #[inline]
263
0
    pub fn into_owned(self) -> FindIter<'h, 'static> {
264
0
        FindIter {
265
0
            haystack: self.haystack,
266
0
            prestate: self.prestate,
267
0
            finder: self.finder.into_owned(),
268
0
            pos: self.pos,
269
0
        }
270
0
    }
271
}
272
273
impl<'h, 'n> Iterator for FindIter<'h, 'n> {
274
    type Item = usize;
275
276
0
    fn next(&mut self) -> Option<usize> {
277
0
        let needle = self.finder.needle();
278
0
        let haystack = self.haystack.get(self.pos..)?;
279
0
        let idx =
280
0
            self.finder.searcher.find(&mut self.prestate, haystack, needle)?;
281
282
0
        let pos = self.pos + idx;
283
0
        self.pos = pos + needle.len().max(1);
284
285
0
        Some(pos)
286
0
    }
287
288
0
    fn size_hint(&self) -> (usize, Option<usize>) {
289
        // The largest possible number of non-overlapping matches is the
290
        // quotient of the haystack and the needle (or the length of the
291
        // haystack, if the needle is empty)
292
0
        match self.haystack.len().checked_sub(self.pos) {
293
0
            None => (0, Some(0)),
294
0
            Some(haystack_len) => match self.finder.needle().len() {
295
                // Empty needles always succeed and match at every point
296
                // (including the very end)
297
0
                0 => (
298
0
                    haystack_len.saturating_add(1),
299
0
                    haystack_len.checked_add(1),
300
0
                ),
301
0
                needle_len => (0, Some(haystack_len / needle_len)),
302
            },
303
        }
304
0
    }
305
}
306
307
/// An iterator over non-overlapping substring matches in reverse.
308
///
309
/// Matches are reported by the byte offset at which they begin.
310
///
311
/// `'h` is the lifetime of the haystack while `'n` is the lifetime of the
312
/// needle.
313
#[derive(Clone, Debug)]
314
pub struct FindRevIter<'h, 'n> {
315
    haystack: &'h [u8],
316
    finder: FinderRev<'n>,
317
    /// When searching with an empty needle, this gets set to `None` after
318
    /// we've yielded the last element at `0`.
319
    pos: Option<usize>,
320
}
321
322
impl<'h, 'n> FindRevIter<'h, 'n> {
323
    #[inline(always)]
324
0
    pub(crate) fn new(
325
0
        haystack: &'h [u8],
326
0
        finder: FinderRev<'n>,
327
0
    ) -> FindRevIter<'h, 'n> {
328
0
        let pos = Some(haystack.len());
329
0
        FindRevIter { haystack, finder, pos }
330
0
    }
331
332
    /// Convert this iterator into its owned variant, such that it no longer
333
    /// borrows the finder and needle.
334
    ///
335
    /// If this is already an owned iterator, then this is a no-op. Otherwise,
336
    /// this copies the needle.
337
    ///
338
    /// This is only available when the `std` feature is enabled.
339
    #[cfg(feature = "alloc")]
340
    #[inline]
341
0
    pub fn into_owned(self) -> FindRevIter<'h, 'static> {
342
0
        FindRevIter {
343
0
            haystack: self.haystack,
344
0
            finder: self.finder.into_owned(),
345
0
            pos: self.pos,
346
0
        }
347
0
    }
348
}
349
350
impl<'h, 'n> Iterator for FindRevIter<'h, 'n> {
351
    type Item = usize;
352
353
0
    fn next(&mut self) -> Option<usize> {
354
0
        let pos = self.pos?;
355
0
        let result = self.finder.rfind(&self.haystack[..pos]);
356
0
        match result {
357
0
            None => None,
358
0
            Some(i) => {
359
0
                if pos == i {
360
0
                    self.pos = pos.checked_sub(1);
361
0
                } else {
362
0
                    self.pos = Some(i);
363
0
                }
364
0
                Some(i)
365
            }
366
        }
367
0
    }
368
}
369
370
/// A single substring searcher fixed to a particular needle.
371
///
372
/// The purpose of this type is to permit callers to construct a substring
373
/// searcher that can be used to search haystacks without the overhead of
374
/// constructing the searcher in the first place. This is a somewhat niche
375
/// concern when it's necessary to re-use the same needle to search multiple
376
/// different haystacks with as little overhead as possible. In general, using
377
/// [`find`] is good enough, but `Finder` is useful when you can meaningfully
378
/// observe searcher construction time in a profile.
379
///
380
/// When the `std` feature is enabled, then this type has an `into_owned`
381
/// version which permits building a `Finder` that is not connected to
382
/// the lifetime of its needle.
383
#[derive(Clone, Debug)]
384
pub struct Finder<'n> {
385
    needle: CowBytes<'n>,
386
    searcher: Searcher,
387
}
388
389
impl<'n> Finder<'n> {
390
    /// Create a new finder for the given needle.
391
    #[inline]
392
0
    pub fn new<B: ?Sized + AsRef<[u8]>>(needle: &'n B) -> Finder<'n> {
393
0
        FinderBuilder::new().build_forward(needle)
394
0
    }
395
396
    /// Returns the index of the first occurrence of this needle in the given
397
    /// haystack.
398
    ///
399
    /// # Complexity
400
    ///
401
    /// This routine is guaranteed to have worst case linear time complexity
402
    /// with respect to both the needle and the haystack. That is, this runs
403
    /// in `O(needle.len() + haystack.len())` time.
404
    ///
405
    /// This routine is also guaranteed to have worst case constant space
406
    /// complexity.
407
    ///
408
    /// # Examples
409
    ///
410
    /// Basic usage:
411
    ///
412
    /// ```
413
    /// use memchr::memmem::Finder;
414
    ///
415
    /// let haystack = b"foo bar baz";
416
    /// assert_eq!(Some(0), Finder::new("foo").find(haystack));
417
    /// assert_eq!(Some(4), Finder::new("bar").find(haystack));
418
    /// assert_eq!(None, Finder::new("quux").find(haystack));
419
    /// ```
420
    #[inline]
421
0
    pub fn find(&self, haystack: &[u8]) -> Option<usize> {
422
0
        let mut prestate = PrefilterState::new();
423
0
        let needle = self.needle.as_slice();
424
0
        self.searcher.find(&mut prestate, haystack, needle)
425
0
    }
426
427
    /// Returns an iterator over all occurrences of a substring in a haystack.
428
    ///
429
    /// # Complexity
430
    ///
431
    /// This routine is guaranteed to have worst case linear time complexity
432
    /// with respect to both the needle and the haystack. That is, this runs
433
    /// in `O(needle.len() + haystack.len())` time.
434
    ///
435
    /// This routine is also guaranteed to have worst case constant space
436
    /// complexity.
437
    ///
438
    /// # Examples
439
    ///
440
    /// Basic usage:
441
    ///
442
    /// ```
443
    /// use memchr::memmem::Finder;
444
    ///
445
    /// let haystack = b"foo bar foo baz foo";
446
    /// let finder = Finder::new(b"foo");
447
    /// let mut it = finder.find_iter(haystack);
448
    /// assert_eq!(Some(0), it.next());
449
    /// assert_eq!(Some(8), it.next());
450
    /// assert_eq!(Some(16), it.next());
451
    /// assert_eq!(None, it.next());
452
    /// ```
453
    #[inline]
454
0
    pub fn find_iter<'a, 'h>(
455
0
        &'a self,
456
0
        haystack: &'h [u8],
457
0
    ) -> FindIter<'h, 'a> {
458
0
        FindIter::new(haystack, self.as_ref())
459
0
    }
460
461
    /// Convert this finder into its owned variant, such that it no longer
462
    /// borrows the needle.
463
    ///
464
    /// If this is already an owned finder, then this is a no-op. Otherwise,
465
    /// this copies the needle.
466
    ///
467
    /// This is only available when the `alloc` feature is enabled.
468
    #[cfg(feature = "alloc")]
469
    #[inline]
470
0
    pub fn into_owned(self) -> Finder<'static> {
471
0
        Finder {
472
0
            needle: self.needle.into_owned(),
473
0
            searcher: self.searcher.clone(),
474
0
        }
475
0
    }
476
477
    /// Convert this finder into its borrowed variant.
478
    ///
479
    /// This is primarily useful if your finder is owned and you'd like to
480
    /// store its borrowed variant in some intermediate data structure.
481
    ///
482
    /// Note that the lifetime parameter of the returned finder is tied to the
483
    /// lifetime of `self`, and may be shorter than the `'n` lifetime of the
484
    /// needle itself. Namely, a finder's needle can be either borrowed or
485
    /// owned, so the lifetime of the needle returned must necessarily be the
486
    /// shorter of the two.
487
    #[inline]
488
0
    pub fn as_ref(&self) -> Finder<'_> {
489
0
        Finder {
490
0
            needle: CowBytes::new(self.needle()),
491
0
            searcher: self.searcher.clone(),
492
0
        }
493
0
    }
494
495
    /// Returns the needle that this finder searches for.
496
    ///
497
    /// Note that the lifetime of the needle returned is tied to the lifetime
498
    /// of the finder, and may be shorter than the `'n` lifetime. Namely, a
499
    /// finder's needle can be either borrowed or owned, so the lifetime of the
500
    /// needle returned must necessarily be the shorter of the two.
501
    #[inline]
502
0
    pub fn needle(&self) -> &[u8] {
503
0
        self.needle.as_slice()
504
0
    }
505
}
506
507
/// A single substring reverse searcher fixed to a particular needle.
508
///
509
/// The purpose of this type is to permit callers to construct a substring
510
/// searcher that can be used to search haystacks without the overhead of
511
/// constructing the searcher in the first place. This is a somewhat niche
512
/// concern when it's necessary to re-use the same needle to search multiple
513
/// different haystacks with as little overhead as possible. In general,
514
/// using [`rfind`] is good enough, but `FinderRev` is useful when you can
515
/// meaningfully observe searcher construction time in a profile.
516
///
517
/// When the `std` feature is enabled, then this type has an `into_owned`
518
/// version which permits building a `FinderRev` that is not connected to
519
/// the lifetime of its needle.
520
#[derive(Clone, Debug)]
521
pub struct FinderRev<'n> {
522
    needle: CowBytes<'n>,
523
    searcher: SearcherRev,
524
}
525
526
impl<'n> FinderRev<'n> {
527
    /// Create a new reverse finder for the given needle.
528
    #[inline]
529
0
    pub fn new<B: ?Sized + AsRef<[u8]>>(needle: &'n B) -> FinderRev<'n> {
530
0
        FinderBuilder::new().build_reverse(needle)
531
0
    }
532
533
    /// Returns the index of the last occurrence of this needle in the given
534
    /// haystack.
535
    ///
536
    /// The haystack may be any type that can be cheaply converted into a
537
    /// `&[u8]`. This includes, but is not limited to, `&str` and `&[u8]`.
538
    ///
539
    /// # Complexity
540
    ///
541
    /// This routine is guaranteed to have worst case linear time complexity
542
    /// with respect to both the needle and the haystack. That is, this runs
543
    /// in `O(needle.len() + haystack.len())` time.
544
    ///
545
    /// This routine is also guaranteed to have worst case constant space
546
    /// complexity.
547
    ///
548
    /// # Examples
549
    ///
550
    /// Basic usage:
551
    ///
552
    /// ```
553
    /// use memchr::memmem::FinderRev;
554
    ///
555
    /// let haystack = b"foo bar baz";
556
    /// assert_eq!(Some(0), FinderRev::new("foo").rfind(haystack));
557
    /// assert_eq!(Some(4), FinderRev::new("bar").rfind(haystack));
558
    /// assert_eq!(None, FinderRev::new("quux").rfind(haystack));
559
    /// ```
560
0
    pub fn rfind<B: AsRef<[u8]>>(&self, haystack: B) -> Option<usize> {
561
0
        self.searcher.rfind(haystack.as_ref(), self.needle.as_slice())
562
0
    }
563
564
    /// Returns a reverse iterator over all occurrences of a substring in a
565
    /// haystack.
566
    ///
567
    /// # Complexity
568
    ///
569
    /// This routine is guaranteed to have worst case linear time complexity
570
    /// with respect to both the needle and the haystack. That is, this runs
571
    /// in `O(needle.len() + haystack.len())` time.
572
    ///
573
    /// This routine is also guaranteed to have worst case constant space
574
    /// complexity.
575
    ///
576
    /// # Examples
577
    ///
578
    /// Basic usage:
579
    ///
580
    /// ```
581
    /// use memchr::memmem::FinderRev;
582
    ///
583
    /// let haystack = b"foo bar foo baz foo";
584
    /// let finder = FinderRev::new(b"foo");
585
    /// let mut it = finder.rfind_iter(haystack);
586
    /// assert_eq!(Some(16), it.next());
587
    /// assert_eq!(Some(8), it.next());
588
    /// assert_eq!(Some(0), it.next());
589
    /// assert_eq!(None, it.next());
590
    /// ```
591
    #[inline]
592
0
    pub fn rfind_iter<'a, 'h>(
593
0
        &'a self,
594
0
        haystack: &'h [u8],
595
0
    ) -> FindRevIter<'h, 'a> {
596
0
        FindRevIter::new(haystack, self.as_ref())
597
0
    }
598
599
    /// Convert this finder into its owned variant, such that it no longer
600
    /// borrows the needle.
601
    ///
602
    /// If this is already an owned finder, then this is a no-op. Otherwise,
603
    /// this copies the needle.
604
    ///
605
    /// This is only available when the `std` feature is enabled.
606
    #[cfg(feature = "alloc")]
607
    #[inline]
608
0
    pub fn into_owned(self) -> FinderRev<'static> {
609
0
        FinderRev {
610
0
            needle: self.needle.into_owned(),
611
0
            searcher: self.searcher.clone(),
612
0
        }
613
0
    }
614
615
    /// Convert this finder into its borrowed variant.
616
    ///
617
    /// This is primarily useful if your finder is owned and you'd like to
618
    /// store its borrowed variant in some intermediate data structure.
619
    ///
620
    /// Note that the lifetime parameter of the returned finder is tied to the
621
    /// lifetime of `self`, and may be shorter than the `'n` lifetime of the
622
    /// needle itself. Namely, a finder's needle can be either borrowed or
623
    /// owned, so the lifetime of the needle returned must necessarily be the
624
    /// shorter of the two.
625
    #[inline]
626
0
    pub fn as_ref(&self) -> FinderRev<'_> {
627
0
        FinderRev {
628
0
            needle: CowBytes::new(self.needle()),
629
0
            searcher: self.searcher.clone(),
630
0
        }
631
0
    }
632
633
    /// Returns the needle that this finder searches for.
634
    ///
635
    /// Note that the lifetime of the needle returned is tied to the lifetime
636
    /// of the finder, and may be shorter than the `'n` lifetime. Namely, a
637
    /// finder's needle can be either borrowed or owned, so the lifetime of the
638
    /// needle returned must necessarily be the shorter of the two.
639
    #[inline]
640
0
    pub fn needle(&self) -> &[u8] {
641
0
        self.needle.as_slice()
642
0
    }
643
}
644
645
/// A builder for constructing non-default forward or reverse memmem finders.
646
///
647
/// A builder is primarily useful for configuring a substring searcher.
648
/// Currently, the only configuration exposed is the ability to disable
649
/// heuristic prefilters used to speed up certain searches.
650
#[derive(Clone, Debug, Default)]
651
pub struct FinderBuilder {
652
    prefilter: Prefilter,
653
}
654
655
impl FinderBuilder {
656
    /// Create a new finder builder with default settings.
657
0
    pub fn new() -> FinderBuilder {
658
0
        FinderBuilder::default()
659
0
    }
660
661
    /// Build a forward finder using the given needle from the current
662
    /// settings.
663
0
    pub fn build_forward<'n, B: ?Sized + AsRef<[u8]>>(
664
0
        &self,
665
0
        needle: &'n B,
666
0
    ) -> Finder<'n> {
667
0
        self.build_forward_with_ranker(DefaultFrequencyRank, needle)
668
0
    }
669
670
    /// Build an owned forward finder using the given needle from the current
671
    /// settings.
672
    #[cfg(feature = "alloc")]
673
0
    pub fn build_forward_owned<B: Into<alloc::boxed::Box<[u8]>>>(
674
0
        &self,
675
0
        needle: B,
676
0
    ) -> Finder<'static> {
677
0
        self.build_forward_with_ranker_owned(DefaultFrequencyRank, needle)
678
0
    }
679
680
    /// Build a forward finder using the given needle and a custom heuristic
681
    /// for determining the frequency of a given byte in the dataset. See
682
    /// [`HeuristicFrequencyRank`] for more details.
683
0
    pub fn build_forward_with_ranker<
684
0
        'n,
685
0
        R: HeuristicFrequencyRank,
686
0
        B: ?Sized + AsRef<[u8]>,
687
0
    >(
688
0
        &self,
689
0
        ranker: R,
690
0
        needle: &'n B,
691
0
    ) -> Finder<'n> {
692
0
        let needle = needle.as_ref();
693
0
        Finder {
694
0
            needle: CowBytes::new(needle),
695
0
            searcher: Searcher::new(self.prefilter, ranker, needle),
696
0
        }
697
0
    }
698
699
    /// Build an owned forward finder using the given needle and a custom
700
    /// heuristic for determining the frequency of a given byte in the dataset.
701
    /// See [`HeuristicFrequencyRank`] for more details.
702
    #[cfg(feature = "alloc")]
703
0
    pub fn build_forward_with_ranker_owned<
704
0
        R: HeuristicFrequencyRank,
705
0
        B: Into<alloc::boxed::Box<[u8]>>,
706
0
    >(
707
0
        &self,
708
0
        ranker: R,
709
0
        needle: B,
710
0
    ) -> Finder<'static> {
711
0
        let needle = needle.into();
712
0
        let searcher = Searcher::new(self.prefilter, ranker, &needle);
713
0
        Finder { needle: CowBytes::new_owned(needle), searcher }
714
0
    }
715
716
    /// Build a reverse finder using the given needle from the current
717
    /// settings.
718
0
    pub fn build_reverse<'n, B: ?Sized + AsRef<[u8]>>(
719
0
        &self,
720
0
        needle: &'n B,
721
0
    ) -> FinderRev<'n> {
722
0
        let needle = needle.as_ref();
723
0
        FinderRev {
724
0
            needle: CowBytes::new(needle),
725
0
            searcher: SearcherRev::new(needle),
726
0
        }
727
0
    }
728
729
    /// Build an owned reverse finder using the given needle from the current
730
    /// settings.
731
    #[cfg(feature = "alloc")]
732
0
    pub fn build_reverse_owned<B: Into<alloc::boxed::Box<[u8]>>>(
733
0
        &self,
734
0
        needle: B,
735
0
    ) -> FinderRev<'static> {
736
0
        let needle = needle.into();
737
0
        let searcher = SearcherRev::new(&needle);
738
0
        FinderRev { needle: CowBytes::new_owned(needle), searcher }
739
0
    }
740
741
    /// Configure the prefilter setting for the finder.
742
    ///
743
    /// See the documentation for [`Prefilter`] for more discussion on why
744
    /// you might want to configure this.
745
0
    pub fn prefilter(&mut self, prefilter: Prefilter) -> &mut FinderBuilder {
746
0
        self.prefilter = prefilter;
747
0
        self
748
0
    }
749
}
750
751
#[cfg(test)]
752
mod tests {
753
    use super::*;
754
755
    define_substring_forward_quickcheck!(|h, n| Some(Finder::new(n).find(h)));
756
    define_substring_reverse_quickcheck!(|h, n| Some(
757
        FinderRev::new(n).rfind(h)
758
    ));
759
760
    #[test]
761
    fn forward() {
762
        crate::tests::substring::Runner::new()
763
            .fwd(|h, n| Some(Finder::new(n).find(h)))
764
            .run();
765
    }
766
767
    #[test]
768
    fn reverse() {
769
        crate::tests::substring::Runner::new()
770
            .rev(|h, n| Some(FinderRev::new(n).rfind(h)))
771
            .run();
772
    }
773
}