1 use std::cmp;
2 use std::io;
3 
4 use crate::line_buffer::{LineBufferReader, DEFAULT_BUFFER_CAPACITY};
5 use crate::lines::{self, LineStep};
6 use crate::sink::{Sink, SinkError};
7 use grep_matcher::Matcher;
8 
9 use crate::searcher::core::Core;
10 use crate::searcher::{Config, Range, Searcher};
11 
12 #[derive(Debug)]
13 pub struct ReadByLine<'s, M, R, S> {
14     config: &'s Config,
15     core: Core<'s, M, S>,
16     rdr: LineBufferReader<'s, R>,
17 }
18 
19 impl<'s, M, R, S> ReadByLine<'s, M, R, S>
20 where
21     M: Matcher,
22     R: io::Read,
23     S: Sink,
24 {
new( searcher: &'s Searcher, matcher: M, read_from: LineBufferReader<'s, R>, write_to: S, ) -> ReadByLine<'s, M, R, S>25     pub fn new(
26         searcher: &'s Searcher,
27         matcher: M,
28         read_from: LineBufferReader<'s, R>,
29         write_to: S,
30     ) -> ReadByLine<'s, M, R, S> {
31         debug_assert!(!searcher.multi_line_with_matcher(&matcher));
32 
33         ReadByLine {
34             config: &searcher.config,
35             core: Core::new(searcher, matcher, write_to, false),
36             rdr: read_from,
37         }
38     }
39 
run(mut self) -> Result<(), S::Error>40     pub fn run(mut self) -> Result<(), S::Error> {
41         if self.core.begin()? {
42             while self.fill()? && self.core.match_by_line(self.rdr.buffer())? {
43             }
44         }
45         self.core.finish(
46             self.rdr.absolute_byte_offset(),
47             self.rdr.binary_byte_offset(),
48         )
49     }
50 
fill(&mut self) -> Result<bool, S::Error>51     fn fill(&mut self) -> Result<bool, S::Error> {
52         assert!(self.rdr.buffer()[self.core.pos()..].is_empty());
53 
54         let already_binary = self.rdr.binary_byte_offset().is_some();
55         let old_buf_len = self.rdr.buffer().len();
56         let consumed = self.core.roll(self.rdr.buffer());
57         self.rdr.consume(consumed);
58         let didread = match self.rdr.fill() {
59             Err(err) => return Err(S::Error::error_io(err)),
60             Ok(didread) => didread,
61         };
62         if !already_binary {
63             if let Some(offset) = self.rdr.binary_byte_offset() {
64                 if !self.core.binary_data(offset)? {
65                     return Ok(false);
66                 }
67             }
68         }
69         if !didread || self.should_binary_quit() {
70             return Ok(false);
71         }
72         // If rolling the buffer didn't result in consuming anything and if
73         // re-filling the buffer didn't add any bytes, then the only thing in
74         // our buffer is leftover context, which we no longer need since there
75         // is nothing left to search. So forcefully quit.
76         if consumed == 0 && old_buf_len == self.rdr.buffer().len() {
77             self.rdr.consume(old_buf_len);
78             return Ok(false);
79         }
80         Ok(true)
81     }
82 
should_binary_quit(&self) -> bool83     fn should_binary_quit(&self) -> bool {
84         self.rdr.binary_byte_offset().is_some()
85             && self.config.binary.quit_byte().is_some()
86     }
87 }
88 
89 #[derive(Debug)]
90 pub struct SliceByLine<'s, M, S> {
91     config: &'s Config,
92     core: Core<'s, M, S>,
93     slice: &'s [u8],
94 }
95 
96 impl<'s, M: Matcher, S: Sink> SliceByLine<'s, M, S> {
new( searcher: &'s Searcher, matcher: M, slice: &'s [u8], write_to: S, ) -> SliceByLine<'s, M, S>97     pub fn new(
98         searcher: &'s Searcher,
99         matcher: M,
100         slice: &'s [u8],
101         write_to: S,
102     ) -> SliceByLine<'s, M, S> {
103         debug_assert!(!searcher.multi_line_with_matcher(&matcher));
104 
105         SliceByLine {
106             config: &searcher.config,
107             core: Core::new(searcher, matcher, write_to, true),
108             slice: slice,
109         }
110     }
111 
run(mut self) -> Result<(), S::Error>112     pub fn run(mut self) -> Result<(), S::Error> {
113         if self.core.begin()? {
114             let binary_upto =
115                 cmp::min(self.slice.len(), DEFAULT_BUFFER_CAPACITY);
116             let binary_range = Range::new(0, binary_upto);
117             if !self.core.detect_binary(self.slice, &binary_range)? {
118                 while !self.slice[self.core.pos()..].is_empty()
119                     && self.core.match_by_line(self.slice)?
120                 {}
121             }
122         }
123         let byte_count = self.byte_count();
124         let binary_byte_offset = self.core.binary_byte_offset();
125         self.core.finish(byte_count, binary_byte_offset)
126     }
127 
byte_count(&mut self) -> u64128     fn byte_count(&mut self) -> u64 {
129         match self.core.binary_byte_offset() {
130             Some(offset) if offset < self.core.pos() as u64 => offset,
131             _ => self.core.pos() as u64,
132         }
133     }
134 }
135 
136 #[derive(Debug)]
137 pub struct MultiLine<'s, M, S> {
138     config: &'s Config,
139     core: Core<'s, M, S>,
140     slice: &'s [u8],
141     last_match: Option<Range>,
142 }
143 
144 impl<'s, M: Matcher, S: Sink> MultiLine<'s, M, S> {
new( searcher: &'s Searcher, matcher: M, slice: &'s [u8], write_to: S, ) -> MultiLine<'s, M, S>145     pub fn new(
146         searcher: &'s Searcher,
147         matcher: M,
148         slice: &'s [u8],
149         write_to: S,
150     ) -> MultiLine<'s, M, S> {
151         debug_assert!(searcher.multi_line_with_matcher(&matcher));
152 
153         MultiLine {
154             config: &searcher.config,
155             core: Core::new(searcher, matcher, write_to, true),
156             slice: slice,
157             last_match: None,
158         }
159     }
160 
run(mut self) -> Result<(), S::Error>161     pub fn run(mut self) -> Result<(), S::Error> {
162         if self.core.begin()? {
163             let binary_upto =
164                 cmp::min(self.slice.len(), DEFAULT_BUFFER_CAPACITY);
165             let binary_range = Range::new(0, binary_upto);
166             if !self.core.detect_binary(self.slice, &binary_range)? {
167                 let mut keepgoing = true;
168                 while !self.slice[self.core.pos()..].is_empty() && keepgoing {
169                     keepgoing = self.sink()?;
170                 }
171                 if keepgoing {
172                     keepgoing = match self.last_match.take() {
173                         None => true,
174                         Some(last_match) => {
175                             if self.sink_context(&last_match)? {
176                                 self.sink_matched(&last_match)?;
177                             }
178                             true
179                         }
180                     };
181                 }
182                 // Take care of any remaining context after the last match.
183                 if keepgoing {
184                     if self.config.passthru {
185                         self.core.other_context_by_line(
186                             self.slice,
187                             self.slice.len(),
188                         )?;
189                     } else {
190                         self.core.after_context_by_line(
191                             self.slice,
192                             self.slice.len(),
193                         )?;
194                     }
195                 }
196             }
197         }
198         let byte_count = self.byte_count();
199         let binary_byte_offset = self.core.binary_byte_offset();
200         self.core.finish(byte_count, binary_byte_offset)
201     }
202 
sink(&mut self) -> Result<bool, S::Error>203     fn sink(&mut self) -> Result<bool, S::Error> {
204         if self.config.invert_match {
205             return self.sink_matched_inverted();
206         }
207         let mat = match self.find()? {
208             Some(range) => range,
209             None => {
210                 self.core.set_pos(self.slice.len());
211                 return Ok(true);
212             }
213         };
214         self.advance(&mat);
215 
216         let line =
217             lines::locate(self.slice, self.config.line_term.as_byte(), mat);
218         // We delay sinking the match to make sure we group adjacent matches
219         // together in a single sink. Adjacent matches are distinct matches
220         // that start and end on the same line, respectively. This guarantees
221         // that a single line is never sinked more than once.
222         match self.last_match.take() {
223             None => {
224                 self.last_match = Some(line);
225                 Ok(true)
226             }
227             Some(last_match) => {
228                 // If the lines in the previous match overlap with the lines
229                 // in this match, then simply grow the match and move on. This
230                 // happens when the next match begins on the same line that the
231                 // last match ends on.
232                 //
233                 // Note that we do not technically require strict overlap here.
234                 // Instead, we only require that the lines are adjacent. This
235                 // provides larger blocks of lines to the printer, and results
236                 // in overall better behavior with respect to how replacements
237                 // are handled.
238                 //
239                 // See: https://github.com/BurntSushi/ripgrep/issues/1311
240                 // And also the associated commit fixing #1311.
241                 if last_match.end() >= line.start() {
242                     self.last_match = Some(last_match.with_end(line.end()));
243                     Ok(true)
244                 } else {
245                     self.last_match = Some(line);
246                     if !self.sink_context(&last_match)? {
247                         return Ok(false);
248                     }
249                     self.sink_matched(&last_match)
250                 }
251             }
252         }
253     }
254 
sink_matched_inverted(&mut self) -> Result<bool, S::Error>255     fn sink_matched_inverted(&mut self) -> Result<bool, S::Error> {
256         assert!(self.config.invert_match);
257 
258         let invert_match = match self.find()? {
259             None => {
260                 let range = Range::new(self.core.pos(), self.slice.len());
261                 self.core.set_pos(range.end());
262                 range
263             }
264             Some(mat) => {
265                 let line = lines::locate(
266                     self.slice,
267                     self.config.line_term.as_byte(),
268                     mat,
269                 );
270                 let range = Range::new(self.core.pos(), line.start());
271                 self.advance(&line);
272                 range
273             }
274         };
275         if invert_match.is_empty() {
276             return Ok(true);
277         }
278         if !self.sink_context(&invert_match)? {
279             return Ok(false);
280         }
281         let mut stepper = LineStep::new(
282             self.config.line_term.as_byte(),
283             invert_match.start(),
284             invert_match.end(),
285         );
286         while let Some(line) = stepper.next_match(self.slice) {
287             if !self.sink_matched(&line)? {
288                 return Ok(false);
289             }
290         }
291         Ok(true)
292     }
293 
sink_matched(&mut self, range: &Range) -> Result<bool, S::Error>294     fn sink_matched(&mut self, range: &Range) -> Result<bool, S::Error> {
295         if range.is_empty() {
296             // The only way we can produce an empty line for a match is if we
297             // match the position immediately following the last byte that we
298             // search, and where that last byte is also the line terminator. We
299             // never want to report that match, and we know we're done at that
300             // point anyway, so stop the search.
301             return Ok(false);
302         }
303         self.core.matched(self.slice, range)
304     }
305 
sink_context(&mut self, range: &Range) -> Result<bool, S::Error>306     fn sink_context(&mut self, range: &Range) -> Result<bool, S::Error> {
307         if self.config.passthru {
308             if !self.core.other_context_by_line(self.slice, range.start())? {
309                 return Ok(false);
310             }
311         } else {
312             if !self.core.after_context_by_line(self.slice, range.start())? {
313                 return Ok(false);
314             }
315             if !self.core.before_context_by_line(self.slice, range.start())? {
316                 return Ok(false);
317             }
318         }
319         Ok(true)
320     }
321 
find(&mut self) -> Result<Option<Range>, S::Error>322     fn find(&mut self) -> Result<Option<Range>, S::Error> {
323         match self.core.matcher().find(&self.slice[self.core.pos()..]) {
324             Err(err) => Err(S::Error::error_message(err)),
325             Ok(None) => Ok(None),
326             Ok(Some(m)) => Ok(Some(m.offset(self.core.pos()))),
327         }
328     }
329 
330     /// Advance the search position based on the previous match.
331     ///
332     /// If the previous match is zero width, then this advances the search
333     /// position one byte past the end of the match.
advance(&mut self, range: &Range)334     fn advance(&mut self, range: &Range) {
335         self.core.set_pos(range.end());
336         if range.is_empty() && self.core.pos() < self.slice.len() {
337             let newpos = self.core.pos() + 1;
338             self.core.set_pos(newpos);
339         }
340     }
341 
byte_count(&mut self) -> u64342     fn byte_count(&mut self) -> u64 {
343         match self.core.binary_byte_offset() {
344             Some(offset) if offset < self.core.pos() as u64 => offset,
345             _ => self.core.pos() as u64,
346         }
347     }
348 }
349 
350 #[cfg(test)]
351 mod tests {
352     use crate::searcher::{BinaryDetection, SearcherBuilder};
353     use crate::testutil::{KitchenSink, RegexMatcher, SearcherTester};
354 
355     use super::*;
356 
357     const SHERLOCK: &'static str = "\
358 For the Doctor Watsons of this world, as opposed to the Sherlock
359 Holmeses, success in the province of detective work must always
360 be, to a very large extent, the result of luck. Sherlock Holmes
361 can extract a clew from a wisp of straw or a flake of cigar ash;
362 but Doctor Watson has to have it taken out for him and dusted,
363 and exhibited clearly, with a label attached.\
364 ";
365 
366     const CODE: &'static str = "\
367 extern crate snap;
368 
369 use std::io;
370 
371 fn main() {
372     let stdin = io::stdin();
373     let stdout = io::stdout();
374 
375     // Wrap the stdin reader in a Snappy reader.
376     let mut rdr = snap::Reader::new(stdin.lock());
377     let mut wtr = stdout.lock();
378     io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
379 }
380 ";
381 
382     #[test]
basic1()383     fn basic1() {
384         let exp = "\
385 0:For the Doctor Watsons of this world, as opposed to the Sherlock
386 129:be, to a very large extent, the result of luck. Sherlock Holmes
387 
388 byte count:366
389 ";
390         SearcherTester::new(SHERLOCK, "Sherlock")
391             .line_number(false)
392             .expected_no_line_number(exp)
393             .test();
394     }
395 
396     #[test]
basic2()397     fn basic2() {
398         let exp = "\nbyte count:366\n";
399         SearcherTester::new(SHERLOCK, "NADA")
400             .line_number(false)
401             .expected_no_line_number(exp)
402             .test();
403     }
404 
405     #[test]
basic3()406     fn basic3() {
407         let exp = "\
408 0:For the Doctor Watsons of this world, as opposed to the Sherlock
409 65:Holmeses, success in the province of detective work must always
410 129:be, to a very large extent, the result of luck. Sherlock Holmes
411 193:can extract a clew from a wisp of straw or a flake of cigar ash;
412 258:but Doctor Watson has to have it taken out for him and dusted,
413 321:and exhibited clearly, with a label attached.
414 byte count:366
415 ";
416         SearcherTester::new(SHERLOCK, "a")
417             .line_number(false)
418             .expected_no_line_number(exp)
419             .test();
420     }
421 
422     #[test]
basic4()423     fn basic4() {
424         let haystack = "\
425 a
426 b
427 
428 c
429 
430 
431 d
432 ";
433         let byte_count = haystack.len();
434         let exp = format!("0:a\n\nbyte count:{}\n", byte_count);
435         SearcherTester::new(haystack, "a")
436             .line_number(false)
437             .expected_no_line_number(&exp)
438             .test();
439     }
440 
441     #[test]
invert1()442     fn invert1() {
443         let exp = "\
444 65:Holmeses, success in the province of detective work must always
445 193:can extract a clew from a wisp of straw or a flake of cigar ash;
446 258:but Doctor Watson has to have it taken out for him and dusted,
447 321:and exhibited clearly, with a label attached.
448 byte count:366
449 ";
450         SearcherTester::new(SHERLOCK, "Sherlock")
451             .line_number(false)
452             .invert_match(true)
453             .expected_no_line_number(exp)
454             .test();
455     }
456 
457     #[test]
line_number1()458     fn line_number1() {
459         let exp = "\
460 0:For the Doctor Watsons of this world, as opposed to the Sherlock
461 129:be, to a very large extent, the result of luck. Sherlock Holmes
462 
463 byte count:366
464 ";
465         let exp_line = "\
466 1:0:For the Doctor Watsons of this world, as opposed to the Sherlock
467 3:129:be, to a very large extent, the result of luck. Sherlock Holmes
468 
469 byte count:366
470 ";
471         SearcherTester::new(SHERLOCK, "Sherlock")
472             .expected_no_line_number(exp)
473             .expected_with_line_number(exp_line)
474             .test();
475     }
476 
477     #[test]
line_number_invert1()478     fn line_number_invert1() {
479         let exp = "\
480 65:Holmeses, success in the province of detective work must always
481 193:can extract a clew from a wisp of straw or a flake of cigar ash;
482 258:but Doctor Watson has to have it taken out for him and dusted,
483 321:and exhibited clearly, with a label attached.
484 byte count:366
485 ";
486         let exp_line = "\
487 2:65:Holmeses, success in the province of detective work must always
488 4:193:can extract a clew from a wisp of straw or a flake of cigar ash;
489 5:258:but Doctor Watson has to have it taken out for him and dusted,
490 6:321:and exhibited clearly, with a label attached.
491 byte count:366
492 ";
493         SearcherTester::new(SHERLOCK, "Sherlock")
494             .invert_match(true)
495             .expected_no_line_number(exp)
496             .expected_with_line_number(exp_line)
497             .test();
498     }
499 
500     #[test]
multi_line_overlap1()501     fn multi_line_overlap1() {
502         let haystack = "xxx\nabc\ndefxxxabc\ndefxxx\nxxx";
503         let byte_count = haystack.len();
504         let exp = format!(
505             "4:abc\n8:defxxxabc\n18:defxxx\n\nbyte count:{}\n",
506             byte_count
507         );
508 
509         SearcherTester::new(haystack, "abc\ndef")
510             .by_line(false)
511             .line_number(false)
512             .expected_no_line_number(&exp)
513             .test();
514     }
515 
516     #[test]
multi_line_overlap2()517     fn multi_line_overlap2() {
518         let haystack = "xxx\nabc\ndefabc\ndefxxx\nxxx";
519         let byte_count = haystack.len();
520         let exp = format!(
521             "4:abc\n8:defabc\n15:defxxx\n\nbyte count:{}\n",
522             byte_count
523         );
524 
525         SearcherTester::new(haystack, "abc\ndef")
526             .by_line(false)
527             .line_number(false)
528             .expected_no_line_number(&exp)
529             .test();
530     }
531 
532     #[test]
empty_line1()533     fn empty_line1() {
534         let exp = "\nbyte count:0\n";
535         SearcherTester::new("", r"^$")
536             .expected_no_line_number(exp)
537             .expected_with_line_number(exp)
538             .test();
539     }
540 
541     #[test]
empty_line2()542     fn empty_line2() {
543         let exp = "0:\n\nbyte count:1\n";
544         let exp_line = "1:0:\n\nbyte count:1\n";
545 
546         SearcherTester::new("\n", r"^$")
547             .expected_no_line_number(exp)
548             .expected_with_line_number(exp_line)
549             .test();
550     }
551 
552     #[test]
empty_line3()553     fn empty_line3() {
554         let exp = "0:\n1:\n\nbyte count:2\n";
555         let exp_line = "1:0:\n2:1:\n\nbyte count:2\n";
556 
557         SearcherTester::new("\n\n", r"^$")
558             .expected_no_line_number(exp)
559             .expected_with_line_number(exp_line)
560             .test();
561     }
562 
563     #[test]
empty_line4()564     fn empty_line4() {
565         // See: https://github.com/BurntSushi/ripgrep/issues/441
566         let haystack = "\
567 a
568 b
569 
570 c
571 
572 
573 d
574 ";
575         let byte_count = haystack.len();
576         let exp = format!("4:\n7:\n8:\n\nbyte count:{}\n", byte_count);
577         let exp_line =
578             format!("3:4:\n5:7:\n6:8:\n\nbyte count:{}\n", byte_count);
579 
580         SearcherTester::new(haystack, r"^$")
581             .expected_no_line_number(&exp)
582             .expected_with_line_number(&exp_line)
583             .test();
584     }
585 
586     #[test]
empty_line5()587     fn empty_line5() {
588         // See: https://github.com/BurntSushi/ripgrep/issues/441
589         // This is like empty_line4, but lacks the trailing line terminator.
590         let haystack = "\
591 a
592 b
593 
594 c
595 
596 
597 d";
598         let byte_count = haystack.len();
599         let exp = format!("4:\n7:\n8:\n\nbyte count:{}\n", byte_count);
600         let exp_line =
601             format!("3:4:\n5:7:\n6:8:\n\nbyte count:{}\n", byte_count);
602 
603         SearcherTester::new(haystack, r"^$")
604             .expected_no_line_number(&exp)
605             .expected_with_line_number(&exp_line)
606             .test();
607     }
608 
609     #[test]
empty_line6()610     fn empty_line6() {
611         // See: https://github.com/BurntSushi/ripgrep/issues/441
612         // This is like empty_line4, but includes an empty line at the end.
613         let haystack = "\
614 a
615 b
616 
617 c
618 
619 
620 d
621 
622 ";
623         let byte_count = haystack.len();
624         let exp = format!("4:\n7:\n8:\n11:\n\nbyte count:{}\n", byte_count);
625         let exp_line =
626             format!("3:4:\n5:7:\n6:8:\n8:11:\n\nbyte count:{}\n", byte_count);
627 
628         SearcherTester::new(haystack, r"^$")
629             .expected_no_line_number(&exp)
630             .expected_with_line_number(&exp_line)
631             .test();
632     }
633 
634     #[test]
big1()635     fn big1() {
636         let mut haystack = String::new();
637         haystack.push_str("a\n");
638         // Pick an arbitrary number above the capacity.
639         for _ in 0..(4 * (DEFAULT_BUFFER_CAPACITY + 7)) {
640             haystack.push_str("zzz\n");
641         }
642         haystack.push_str("a\n");
643 
644         let byte_count = haystack.len();
645         let exp = format!("0:a\n1048690:a\n\nbyte count:{}\n", byte_count);
646 
647         SearcherTester::new(&haystack, "a")
648             .line_number(false)
649             .expected_no_line_number(&exp)
650             .test();
651     }
652 
653     #[test]
big_error_one_line()654     fn big_error_one_line() {
655         let mut haystack = String::new();
656         haystack.push_str("a\n");
657         // Pick an arbitrary number above the capacity.
658         for _ in 0..(4 * (DEFAULT_BUFFER_CAPACITY + 7)) {
659             haystack.push_str("zzz\n");
660         }
661         haystack.push_str("a\n");
662 
663         let matcher = RegexMatcher::new("a");
664         let mut sink = KitchenSink::new();
665         let mut searcher = SearcherBuilder::new()
666             .heap_limit(Some(3)) // max line length is 4, one byte short
667             .build();
668         let result =
669             searcher.search_reader(&matcher, haystack.as_bytes(), &mut sink);
670         assert!(result.is_err());
671     }
672 
673     #[test]
big_error_multi_line()674     fn big_error_multi_line() {
675         let mut haystack = String::new();
676         haystack.push_str("a\n");
677         // Pick an arbitrary number above the capacity.
678         for _ in 0..(4 * (DEFAULT_BUFFER_CAPACITY + 7)) {
679             haystack.push_str("zzz\n");
680         }
681         haystack.push_str("a\n");
682 
683         let matcher = RegexMatcher::new("a");
684         let mut sink = KitchenSink::new();
685         let mut searcher = SearcherBuilder::new()
686             .multi_line(true)
687             .heap_limit(Some(haystack.len())) // actually need one more byte
688             .build();
689         let result =
690             searcher.search_reader(&matcher, haystack.as_bytes(), &mut sink);
691         assert!(result.is_err());
692     }
693 
694     #[test]
binary1()695     fn binary1() {
696         let haystack = "\x00a";
697         let exp = "\nbyte count:0\nbinary offset:0\n";
698 
699         SearcherTester::new(haystack, "a")
700             .binary_detection(BinaryDetection::quit(0))
701             .line_number(false)
702             .expected_no_line_number(exp)
703             .test();
704     }
705 
706     #[test]
binary2()707     fn binary2() {
708         let haystack = "a\x00";
709         let exp = "\nbyte count:0\nbinary offset:1\n";
710 
711         SearcherTester::new(haystack, "a")
712             .binary_detection(BinaryDetection::quit(0))
713             .line_number(false)
714             .expected_no_line_number(exp)
715             .test();
716     }
717 
718     #[test]
binary3()719     fn binary3() {
720         let mut haystack = String::new();
721         haystack.push_str("a\n");
722         for _ in 0..DEFAULT_BUFFER_CAPACITY {
723             haystack.push_str("zzz\n");
724         }
725         haystack.push_str("a\n");
726         haystack.push_str("zzz\n");
727         haystack.push_str("a\x00a\n");
728         haystack.push_str("zzz\n");
729         haystack.push_str("a\n");
730 
731         // The line buffered searcher has slightly different semantics here.
732         // Namely, it will *always* detect binary data in the current buffer
733         // before searching it. Thus, the total number of bytes searched is
734         // smaller than below.
735         let exp = "0:a\n\nbyte count:262146\nbinary offset:262153\n";
736         // In contrast, the slice readers (for multi line as well) will only
737         // look for binary data in the initial chunk of bytes. After that
738         // point, it only looks for binary data in matches. Note though that
739         // the binary offset remains the same. (See the binary4 test for a case
740         // where the offset is explicitly different.)
741         let exp_slice =
742             "0:a\n262146:a\n\nbyte count:262153\nbinary offset:262153\n";
743 
744         SearcherTester::new(&haystack, "a")
745             .binary_detection(BinaryDetection::quit(0))
746             .line_number(false)
747             .auto_heap_limit(false)
748             .expected_no_line_number(exp)
749             .expected_slice_no_line_number(exp_slice)
750             .test();
751     }
752 
753     #[test]
binary4()754     fn binary4() {
755         let mut haystack = String::new();
756         haystack.push_str("a\n");
757         for _ in 0..DEFAULT_BUFFER_CAPACITY {
758             haystack.push_str("zzz\n");
759         }
760         haystack.push_str("a\n");
761         // The Read searcher will detect binary data here, but since this is
762         // beyond the initial buffer size and doesn't otherwise contain a
763         // match, the Slice reader won't detect the binary data until the next
764         // line (which is a match).
765         haystack.push_str("b\x00b\n");
766         haystack.push_str("a\x00a\n");
767         haystack.push_str("a\n");
768 
769         let exp = "0:a\n\nbyte count:262146\nbinary offset:262149\n";
770         // The binary offset for the Slice readers corresponds to the binary
771         // data in `a\x00a\n` since the first line with binary data
772         // (`b\x00b\n`) isn't part of a match, and is therefore undetected.
773         let exp_slice =
774             "0:a\n262146:a\n\nbyte count:262153\nbinary offset:262153\n";
775 
776         SearcherTester::new(&haystack, "a")
777             .binary_detection(BinaryDetection::quit(0))
778             .line_number(false)
779             .auto_heap_limit(false)
780             .expected_no_line_number(exp)
781             .expected_slice_no_line_number(exp_slice)
782             .test();
783     }
784 
785     #[test]
passthru_sherlock1()786     fn passthru_sherlock1() {
787         let exp = "\
788 0:For the Doctor Watsons of this world, as opposed to the Sherlock
789 65-Holmeses, success in the province of detective work must always
790 129:be, to a very large extent, the result of luck. Sherlock Holmes
791 193-can extract a clew from a wisp of straw or a flake of cigar ash;
792 258-but Doctor Watson has to have it taken out for him and dusted,
793 321-and exhibited clearly, with a label attached.
794 byte count:366
795 ";
796         SearcherTester::new(SHERLOCK, "Sherlock")
797             .passthru(true)
798             .line_number(false)
799             .expected_no_line_number(exp)
800             .test();
801     }
802 
803     #[test]
passthru_sherlock_invert1()804     fn passthru_sherlock_invert1() {
805         let exp = "\
806 0-For the Doctor Watsons of this world, as opposed to the Sherlock
807 65:Holmeses, success in the province of detective work must always
808 129-be, to a very large extent, the result of luck. Sherlock Holmes
809 193:can extract a clew from a wisp of straw or a flake of cigar ash;
810 258:but Doctor Watson has to have it taken out for him and dusted,
811 321:and exhibited clearly, with a label attached.
812 byte count:366
813 ";
814         SearcherTester::new(SHERLOCK, "Sherlock")
815             .passthru(true)
816             .line_number(false)
817             .invert_match(true)
818             .expected_no_line_number(exp)
819             .test();
820     }
821 
822     #[test]
context_sherlock1()823     fn context_sherlock1() {
824         let exp = "\
825 0:For the Doctor Watsons of this world, as opposed to the Sherlock
826 65-Holmeses, success in the province of detective work must always
827 129:be, to a very large extent, the result of luck. Sherlock Holmes
828 193-can extract a clew from a wisp of straw or a flake of cigar ash;
829 
830 byte count:366
831 ";
832         let exp_lines = "\
833 1:0:For the Doctor Watsons of this world, as opposed to the Sherlock
834 2-65-Holmeses, success in the province of detective work must always
835 3:129:be, to a very large extent, the result of luck. Sherlock Holmes
836 4-193-can extract a clew from a wisp of straw or a flake of cigar ash;
837 
838 byte count:366
839 ";
840         // before and after + line numbers
841         SearcherTester::new(SHERLOCK, "Sherlock")
842             .after_context(1)
843             .before_context(1)
844             .line_number(true)
845             .expected_no_line_number(exp)
846             .expected_with_line_number(exp_lines)
847             .test();
848 
849         // after
850         SearcherTester::new(SHERLOCK, "Sherlock")
851             .after_context(1)
852             .line_number(false)
853             .expected_no_line_number(exp)
854             .test();
855 
856         // before
857         let exp = "\
858 0:For the Doctor Watsons of this world, as opposed to the Sherlock
859 65-Holmeses, success in the province of detective work must always
860 129:be, to a very large extent, the result of luck. Sherlock Holmes
861 
862 byte count:366
863 ";
864         SearcherTester::new(SHERLOCK, "Sherlock")
865             .before_context(1)
866             .line_number(false)
867             .expected_no_line_number(exp)
868             .test();
869     }
870 
871     #[test]
context_sherlock_invert1()872     fn context_sherlock_invert1() {
873         let exp = "\
874 0-For the Doctor Watsons of this world, as opposed to the Sherlock
875 65:Holmeses, success in the province of detective work must always
876 129-be, to a very large extent, the result of luck. Sherlock Holmes
877 193:can extract a clew from a wisp of straw or a flake of cigar ash;
878 258:but Doctor Watson has to have it taken out for him and dusted,
879 321:and exhibited clearly, with a label attached.
880 byte count:366
881 ";
882         let exp_lines = "\
883 1-0-For the Doctor Watsons of this world, as opposed to the Sherlock
884 2:65:Holmeses, success in the province of detective work must always
885 3-129-be, to a very large extent, the result of luck. Sherlock Holmes
886 4:193:can extract a clew from a wisp of straw or a flake of cigar ash;
887 5:258:but Doctor Watson has to have it taken out for him and dusted,
888 6:321:and exhibited clearly, with a label attached.
889 byte count:366
890 ";
891         // before and after + line numbers
892         SearcherTester::new(SHERLOCK, "Sherlock")
893             .after_context(1)
894             .before_context(1)
895             .line_number(true)
896             .invert_match(true)
897             .expected_no_line_number(exp)
898             .expected_with_line_number(exp_lines)
899             .test();
900 
901         // before
902         SearcherTester::new(SHERLOCK, "Sherlock")
903             .before_context(1)
904             .line_number(false)
905             .invert_match(true)
906             .expected_no_line_number(exp)
907             .test();
908 
909         // after
910         let exp = "\
911 65:Holmeses, success in the province of detective work must always
912 129-be, to a very large extent, the result of luck. Sherlock Holmes
913 193:can extract a clew from a wisp of straw or a flake of cigar ash;
914 258:but Doctor Watson has to have it taken out for him and dusted,
915 321:and exhibited clearly, with a label attached.
916 byte count:366
917 ";
918         SearcherTester::new(SHERLOCK, "Sherlock")
919             .after_context(1)
920             .line_number(false)
921             .invert_match(true)
922             .expected_no_line_number(exp)
923             .test();
924     }
925 
926     #[test]
context_sherlock2()927     fn context_sherlock2() {
928         let exp = "\
929 65-Holmeses, success in the province of detective work must always
930 129:be, to a very large extent, the result of luck. Sherlock Holmes
931 193:can extract a clew from a wisp of straw or a flake of cigar ash;
932 258-but Doctor Watson has to have it taken out for him and dusted,
933 321:and exhibited clearly, with a label attached.
934 byte count:366
935 ";
936         let exp_lines = "\
937 2-65-Holmeses, success in the province of detective work must always
938 3:129:be, to a very large extent, the result of luck. Sherlock Holmes
939 4:193:can extract a clew from a wisp of straw or a flake of cigar ash;
940 5-258-but Doctor Watson has to have it taken out for him and dusted,
941 6:321:and exhibited clearly, with a label attached.
942 byte count:366
943 ";
944         // before + after + line numbers
945         SearcherTester::new(SHERLOCK, " a ")
946             .after_context(1)
947             .before_context(1)
948             .line_number(true)
949             .expected_no_line_number(exp)
950             .expected_with_line_number(exp_lines)
951             .test();
952 
953         // before
954         SearcherTester::new(SHERLOCK, " a ")
955             .before_context(1)
956             .line_number(false)
957             .expected_no_line_number(exp)
958             .test();
959 
960         // after
961         let exp = "\
962 129:be, to a very large extent, the result of luck. Sherlock Holmes
963 193:can extract a clew from a wisp of straw or a flake of cigar ash;
964 258-but Doctor Watson has to have it taken out for him and dusted,
965 321:and exhibited clearly, with a label attached.
966 byte count:366
967 ";
968         SearcherTester::new(SHERLOCK, " a ")
969             .after_context(1)
970             .line_number(false)
971             .expected_no_line_number(exp)
972             .test();
973     }
974 
975     #[test]
context_sherlock_invert2()976     fn context_sherlock_invert2() {
977         let exp = "\
978 0:For the Doctor Watsons of this world, as opposed to the Sherlock
979 65:Holmeses, success in the province of detective work must always
980 129-be, to a very large extent, the result of luck. Sherlock Holmes
981 193-can extract a clew from a wisp of straw or a flake of cigar ash;
982 258:but Doctor Watson has to have it taken out for him and dusted,
983 321-and exhibited clearly, with a label attached.
984 byte count:366
985 ";
986         let exp_lines = "\
987 1:0:For the Doctor Watsons of this world, as opposed to the Sherlock
988 2:65:Holmeses, success in the province of detective work must always
989 3-129-be, to a very large extent, the result of luck. Sherlock Holmes
990 4-193-can extract a clew from a wisp of straw or a flake of cigar ash;
991 5:258:but Doctor Watson has to have it taken out for him and dusted,
992 6-321-and exhibited clearly, with a label attached.
993 byte count:366
994 ";
995         // before + after + line numbers
996         SearcherTester::new(SHERLOCK, " a ")
997             .after_context(1)
998             .before_context(1)
999             .line_number(true)
1000             .invert_match(true)
1001             .expected_no_line_number(exp)
1002             .expected_with_line_number(exp_lines)
1003             .test();
1004 
1005         // before
1006         let exp = "\
1007 0:For the Doctor Watsons of this world, as opposed to the Sherlock
1008 65:Holmeses, success in the province of detective work must always
1009 --
1010 193-can extract a clew from a wisp of straw or a flake of cigar ash;
1011 258:but Doctor Watson has to have it taken out for him and dusted,
1012 
1013 byte count:366
1014 ";
1015         SearcherTester::new(SHERLOCK, " a ")
1016             .before_context(1)
1017             .line_number(false)
1018             .invert_match(true)
1019             .expected_no_line_number(exp)
1020             .test();
1021 
1022         // after
1023         let exp = "\
1024 0:For the Doctor Watsons of this world, as opposed to the Sherlock
1025 65:Holmeses, success in the province of detective work must always
1026 129-be, to a very large extent, the result of luck. Sherlock Holmes
1027 --
1028 258:but Doctor Watson has to have it taken out for him and dusted,
1029 321-and exhibited clearly, with a label attached.
1030 byte count:366
1031 ";
1032         SearcherTester::new(SHERLOCK, " a ")
1033             .after_context(1)
1034             .line_number(false)
1035             .invert_match(true)
1036             .expected_no_line_number(exp)
1037             .test();
1038     }
1039 
1040     #[test]
context_sherlock3()1041     fn context_sherlock3() {
1042         let exp = "\
1043 0:For the Doctor Watsons of this world, as opposed to the Sherlock
1044 65-Holmeses, success in the province of detective work must always
1045 129:be, to a very large extent, the result of luck. Sherlock Holmes
1046 193-can extract a clew from a wisp of straw or a flake of cigar ash;
1047 258-but Doctor Watson has to have it taken out for him and dusted,
1048 
1049 byte count:366
1050 ";
1051         let exp_lines = "\
1052 1:0:For the Doctor Watsons of this world, as opposed to the Sherlock
1053 2-65-Holmeses, success in the province of detective work must always
1054 3:129:be, to a very large extent, the result of luck. Sherlock Holmes
1055 4-193-can extract a clew from a wisp of straw or a flake of cigar ash;
1056 5-258-but Doctor Watson has to have it taken out for him and dusted,
1057 
1058 byte count:366
1059 ";
1060         // before and after + line numbers
1061         SearcherTester::new(SHERLOCK, "Sherlock")
1062             .after_context(2)
1063             .before_context(2)
1064             .line_number(true)
1065             .expected_no_line_number(exp)
1066             .expected_with_line_number(exp_lines)
1067             .test();
1068 
1069         // after
1070         SearcherTester::new(SHERLOCK, "Sherlock")
1071             .after_context(2)
1072             .line_number(false)
1073             .expected_no_line_number(exp)
1074             .test();
1075 
1076         // before
1077         let exp = "\
1078 0:For the Doctor Watsons of this world, as opposed to the Sherlock
1079 65-Holmeses, success in the province of detective work must always
1080 129:be, to a very large extent, the result of luck. Sherlock Holmes
1081 
1082 byte count:366
1083 ";
1084         SearcherTester::new(SHERLOCK, "Sherlock")
1085             .before_context(2)
1086             .line_number(false)
1087             .expected_no_line_number(exp)
1088             .test();
1089     }
1090 
1091     #[test]
context_sherlock4()1092     fn context_sherlock4() {
1093         let exp = "\
1094 129-be, to a very large extent, the result of luck. Sherlock Holmes
1095 193-can extract a clew from a wisp of straw or a flake of cigar ash;
1096 258:but Doctor Watson has to have it taken out for him and dusted,
1097 321-and exhibited clearly, with a label attached.
1098 byte count:366
1099 ";
1100         let exp_lines = "\
1101 3-129-be, to a very large extent, the result of luck. Sherlock Holmes
1102 4-193-can extract a clew from a wisp of straw or a flake of cigar ash;
1103 5:258:but Doctor Watson has to have it taken out for him and dusted,
1104 6-321-and exhibited clearly, with a label attached.
1105 byte count:366
1106 ";
1107         // before and after + line numbers
1108         SearcherTester::new(SHERLOCK, "dusted")
1109             .after_context(2)
1110             .before_context(2)
1111             .line_number(true)
1112             .expected_no_line_number(exp)
1113             .expected_with_line_number(exp_lines)
1114             .test();
1115 
1116         // after
1117         let exp = "\
1118 258:but Doctor Watson has to have it taken out for him and dusted,
1119 321-and exhibited clearly, with a label attached.
1120 byte count:366
1121 ";
1122         SearcherTester::new(SHERLOCK, "dusted")
1123             .after_context(2)
1124             .line_number(false)
1125             .expected_no_line_number(exp)
1126             .test();
1127 
1128         // before
1129         let exp = "\
1130 129-be, to a very large extent, the result of luck. Sherlock Holmes
1131 193-can extract a clew from a wisp of straw or a flake of cigar ash;
1132 258:but Doctor Watson has to have it taken out for him and dusted,
1133 
1134 byte count:366
1135 ";
1136         SearcherTester::new(SHERLOCK, "dusted")
1137             .before_context(2)
1138             .line_number(false)
1139             .expected_no_line_number(exp)
1140             .test();
1141     }
1142 
1143     #[test]
context_sherlock5()1144     fn context_sherlock5() {
1145         let exp = "\
1146 0-For the Doctor Watsons of this world, as opposed to the Sherlock
1147 65:Holmeses, success in the province of detective work must always
1148 129-be, to a very large extent, the result of luck. Sherlock Holmes
1149 193-can extract a clew from a wisp of straw or a flake of cigar ash;
1150 258-but Doctor Watson has to have it taken out for him and dusted,
1151 321:and exhibited clearly, with a label attached.
1152 byte count:366
1153 ";
1154         let exp_lines = "\
1155 1-0-For the Doctor Watsons of this world, as opposed to the Sherlock
1156 2:65:Holmeses, success in the province of detective work must always
1157 3-129-be, to a very large extent, the result of luck. Sherlock Holmes
1158 4-193-can extract a clew from a wisp of straw or a flake of cigar ash;
1159 5-258-but Doctor Watson has to have it taken out for him and dusted,
1160 6:321:and exhibited clearly, with a label attached.
1161 byte count:366
1162 ";
1163         // before and after + line numbers
1164         SearcherTester::new(SHERLOCK, "success|attached")
1165             .after_context(2)
1166             .before_context(2)
1167             .line_number(true)
1168             .expected_no_line_number(exp)
1169             .expected_with_line_number(exp_lines)
1170             .test();
1171 
1172         // after
1173         let exp = "\
1174 65:Holmeses, success in the province of detective work must always
1175 129-be, to a very large extent, the result of luck. Sherlock Holmes
1176 193-can extract a clew from a wisp of straw or a flake of cigar ash;
1177 --
1178 321:and exhibited clearly, with a label attached.
1179 byte count:366
1180 ";
1181         SearcherTester::new(SHERLOCK, "success|attached")
1182             .after_context(2)
1183             .line_number(false)
1184             .expected_no_line_number(exp)
1185             .test();
1186 
1187         // before
1188         let exp = "\
1189 0-For the Doctor Watsons of this world, as opposed to the Sherlock
1190 65:Holmeses, success in the province of detective work must always
1191 --
1192 193-can extract a clew from a wisp of straw or a flake of cigar ash;
1193 258-but Doctor Watson has to have it taken out for him and dusted,
1194 321:and exhibited clearly, with a label attached.
1195 byte count:366
1196 ";
1197         SearcherTester::new(SHERLOCK, "success|attached")
1198             .before_context(2)
1199             .line_number(false)
1200             .expected_no_line_number(exp)
1201             .test();
1202     }
1203 
1204     #[test]
context_sherlock6()1205     fn context_sherlock6() {
1206         let exp = "\
1207 0:For the Doctor Watsons of this world, as opposed to the Sherlock
1208 65-Holmeses, success in the province of detective work must always
1209 129:be, to a very large extent, the result of luck. Sherlock Holmes
1210 193-can extract a clew from a wisp of straw or a flake of cigar ash;
1211 258-but Doctor Watson has to have it taken out for him and dusted,
1212 321-and exhibited clearly, with a label attached.
1213 byte count:366
1214 ";
1215         let exp_lines = "\
1216 1:0:For the Doctor Watsons of this world, as opposed to the Sherlock
1217 2-65-Holmeses, success in the province of detective work must always
1218 3:129:be, to a very large extent, the result of luck. Sherlock Holmes
1219 4-193-can extract a clew from a wisp of straw or a flake of cigar ash;
1220 5-258-but Doctor Watson has to have it taken out for him and dusted,
1221 6-321-and exhibited clearly, with a label attached.
1222 byte count:366
1223 ";
1224         // before and after + line numbers
1225         SearcherTester::new(SHERLOCK, "Sherlock")
1226             .after_context(3)
1227             .before_context(3)
1228             .line_number(true)
1229             .expected_no_line_number(exp)
1230             .expected_with_line_number(exp_lines)
1231             .test();
1232 
1233         // after
1234         let exp = "\
1235 0:For the Doctor Watsons of this world, as opposed to the Sherlock
1236 65-Holmeses, success in the province of detective work must always
1237 129:be, to a very large extent, the result of luck. Sherlock Holmes
1238 193-can extract a clew from a wisp of straw or a flake of cigar ash;
1239 258-but Doctor Watson has to have it taken out for him and dusted,
1240 321-and exhibited clearly, with a label attached.
1241 byte count:366
1242 ";
1243         SearcherTester::new(SHERLOCK, "Sherlock")
1244             .after_context(3)
1245             .line_number(false)
1246             .expected_no_line_number(exp)
1247             .test();
1248 
1249         // before
1250         let exp = "\
1251 0:For the Doctor Watsons of this world, as opposed to the Sherlock
1252 65-Holmeses, success in the province of detective work must always
1253 129:be, to a very large extent, the result of luck. Sherlock Holmes
1254 
1255 byte count:366
1256 ";
1257         SearcherTester::new(SHERLOCK, "Sherlock")
1258             .before_context(3)
1259             .line_number(false)
1260             .expected_no_line_number(exp)
1261             .test();
1262     }
1263 
1264     #[test]
context_code1()1265     fn context_code1() {
1266         // before and after
1267         let exp = "\
1268 33-
1269 34-fn main() {
1270 46:    let stdin = io::stdin();
1271 75-    let stdout = io::stdout();
1272 106-
1273 107:    // Wrap the stdin reader in a Snappy reader.
1274 156:    let mut rdr = snap::Reader::new(stdin.lock());
1275 207-    let mut wtr = stdout.lock();
1276 240-    io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
1277 
1278 byte count:307
1279 ";
1280         let exp_lines = "\
1281 4-33-
1282 5-34-fn main() {
1283 6:46:    let stdin = io::stdin();
1284 7-75-    let stdout = io::stdout();
1285 8-106-
1286 9:107:    // Wrap the stdin reader in a Snappy reader.
1287 10:156:    let mut rdr = snap::Reader::new(stdin.lock());
1288 11-207-    let mut wtr = stdout.lock();
1289 12-240-    io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
1290 
1291 byte count:307
1292 ";
1293         // before and after + line numbers
1294         SearcherTester::new(CODE, "stdin")
1295             .after_context(2)
1296             .before_context(2)
1297             .line_number(true)
1298             .expected_no_line_number(exp)
1299             .expected_with_line_number(exp_lines)
1300             .test();
1301 
1302         // after
1303         let exp = "\
1304 46:    let stdin = io::stdin();
1305 75-    let stdout = io::stdout();
1306 106-
1307 107:    // Wrap the stdin reader in a Snappy reader.
1308 156:    let mut rdr = snap::Reader::new(stdin.lock());
1309 207-    let mut wtr = stdout.lock();
1310 240-    io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
1311 
1312 byte count:307
1313 ";
1314         SearcherTester::new(CODE, "stdin")
1315             .after_context(2)
1316             .line_number(false)
1317             .expected_no_line_number(exp)
1318             .test();
1319 
1320         // before
1321         let exp = "\
1322 33-
1323 34-fn main() {
1324 46:    let stdin = io::stdin();
1325 75-    let stdout = io::stdout();
1326 106-
1327 107:    // Wrap the stdin reader in a Snappy reader.
1328 156:    let mut rdr = snap::Reader::new(stdin.lock());
1329 
1330 byte count:307
1331 ";
1332         SearcherTester::new(CODE, "stdin")
1333             .before_context(2)
1334             .line_number(false)
1335             .expected_no_line_number(exp)
1336             .test();
1337     }
1338 
1339     #[test]
context_code2()1340     fn context_code2() {
1341         let exp = "\
1342 34-fn main() {
1343 46-    let stdin = io::stdin();
1344 75:    let stdout = io::stdout();
1345 106-
1346 107-    // Wrap the stdin reader in a Snappy reader.
1347 156-    let mut rdr = snap::Reader::new(stdin.lock());
1348 207:    let mut wtr = stdout.lock();
1349 240-    io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
1350 305-}
1351 
1352 byte count:307
1353 ";
1354         let exp_lines = "\
1355 5-34-fn main() {
1356 6-46-    let stdin = io::stdin();
1357 7:75:    let stdout = io::stdout();
1358 8-106-
1359 9-107-    // Wrap the stdin reader in a Snappy reader.
1360 10-156-    let mut rdr = snap::Reader::new(stdin.lock());
1361 11:207:    let mut wtr = stdout.lock();
1362 12-240-    io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
1363 13-305-}
1364 
1365 byte count:307
1366 ";
1367         // before and after + line numbers
1368         SearcherTester::new(CODE, "stdout")
1369             .after_context(2)
1370             .before_context(2)
1371             .line_number(true)
1372             .expected_no_line_number(exp)
1373             .expected_with_line_number(exp_lines)
1374             .test();
1375 
1376         // after
1377         let exp = "\
1378 75:    let stdout = io::stdout();
1379 106-
1380 107-    // Wrap the stdin reader in a Snappy reader.
1381 --
1382 207:    let mut wtr = stdout.lock();
1383 240-    io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
1384 305-}
1385 
1386 byte count:307
1387 ";
1388         SearcherTester::new(CODE, "stdout")
1389             .after_context(2)
1390             .line_number(false)
1391             .expected_no_line_number(exp)
1392             .test();
1393 
1394         // before
1395         let exp = "\
1396 34-fn main() {
1397 46-    let stdin = io::stdin();
1398 75:    let stdout = io::stdout();
1399 --
1400 107-    // Wrap the stdin reader in a Snappy reader.
1401 156-    let mut rdr = snap::Reader::new(stdin.lock());
1402 207:    let mut wtr = stdout.lock();
1403 
1404 byte count:307
1405 ";
1406         SearcherTester::new(CODE, "stdout")
1407             .before_context(2)
1408             .line_number(false)
1409             .expected_no_line_number(exp)
1410             .test();
1411     }
1412 
1413     #[test]
context_code3()1414     fn context_code3() {
1415         let exp = "\
1416 20-use std::io;
1417 33-
1418 34:fn main() {
1419 46-    let stdin = io::stdin();
1420 75-    let stdout = io::stdout();
1421 106-
1422 107-    // Wrap the stdin reader in a Snappy reader.
1423 156:    let mut rdr = snap::Reader::new(stdin.lock());
1424 207-    let mut wtr = stdout.lock();
1425 240-    io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
1426 
1427 byte count:307
1428 ";
1429         let exp_lines = "\
1430 3-20-use std::io;
1431 4-33-
1432 5:34:fn main() {
1433 6-46-    let stdin = io::stdin();
1434 7-75-    let stdout = io::stdout();
1435 8-106-
1436 9-107-    // Wrap the stdin reader in a Snappy reader.
1437 10:156:    let mut rdr = snap::Reader::new(stdin.lock());
1438 11-207-    let mut wtr = stdout.lock();
1439 12-240-    io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
1440 
1441 byte count:307
1442 ";
1443         // before and after + line numbers
1444         SearcherTester::new(CODE, "fn main|let mut rdr")
1445             .after_context(2)
1446             .before_context(2)
1447             .line_number(true)
1448             .expected_no_line_number(exp)
1449             .expected_with_line_number(exp_lines)
1450             .test();
1451 
1452         // after
1453         let exp = "\
1454 34:fn main() {
1455 46-    let stdin = io::stdin();
1456 75-    let stdout = io::stdout();
1457 --
1458 156:    let mut rdr = snap::Reader::new(stdin.lock());
1459 207-    let mut wtr = stdout.lock();
1460 240-    io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
1461 
1462 byte count:307
1463 ";
1464         SearcherTester::new(CODE, "fn main|let mut rdr")
1465             .after_context(2)
1466             .line_number(false)
1467             .expected_no_line_number(exp)
1468             .test();
1469 
1470         // before
1471         let exp = "\
1472 20-use std::io;
1473 33-
1474 34:fn main() {
1475 --
1476 106-
1477 107-    // Wrap the stdin reader in a Snappy reader.
1478 156:    let mut rdr = snap::Reader::new(stdin.lock());
1479 
1480 byte count:307
1481 ";
1482         SearcherTester::new(CODE, "fn main|let mut rdr")
1483             .before_context(2)
1484             .line_number(false)
1485             .expected_no_line_number(exp)
1486             .test();
1487     }
1488 
1489     #[test]
scratch()1490     fn scratch() {
1491         use crate::sinks;
1492         use crate::testutil::RegexMatcher;
1493 
1494         const SHERLOCK: &'static [u8] = b"\
1495 For the Doctor Wat\xFFsons of this world, as opposed to the Sherlock
1496 Holmeses, success in the province of detective work must always
1497 be, to a very large extent, the result of luck. Sherlock Holmes
1498 can extract a clew from a wisp of straw or a flake of cigar ash;
1499 but Doctor Watson has to have it taken out for him and dusted,
1500 and exhibited clearly, with a label attached.\
1501     ";
1502 
1503         let haystack = SHERLOCK;
1504         let matcher = RegexMatcher::new("Sherlock");
1505         let mut searcher = SearcherBuilder::new().line_number(true).build();
1506         searcher
1507             .search_reader(
1508                 &matcher,
1509                 haystack,
1510                 sinks::Lossy(|n, line| {
1511                     print!("{}:{}", n, line);
1512                     Ok(true)
1513                 }),
1514             )
1515             .unwrap();
1516     }
1517 }
1518