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