@@ -405,6 +405,143 @@ contains |
| 405 | 405 | |
| 406 | 406 | end function to_lower |
| 407 | 407 | |
| 408 | + subroutine match_lines_batch(src, batch, patterns, opts, compiled, match_results) |
| 409 | + !> Match a batch of lines against patterns |
| 410 | + !> Returns array of match results (true/false for each line) |
| 411 | + type(input_source), intent(in) :: src |
| 412 | + type(line_batch_t), intent(in) :: batch |
| 413 | + character(len=max_pattern_len), intent(in) :: patterns(:) |
| 414 | + type(grep_options), intent(in) :: opts |
| 415 | + type(compiled_patterns_t), intent(inout) :: compiled |
| 416 | + logical, intent(out) :: match_results(BATCH_SIZE) |
| 417 | + |
| 418 | + integer :: i |
| 419 | + character(len=:), allocatable :: line |
| 420 | + |
| 421 | + match_results = .false. |
| 422 | + |
| 423 | + do i = 1, batch%count |
| 424 | + ! Extract line text from mmap |
| 425 | + line = src%get_line_text(batch%lines(i)) |
| 426 | + ! Match this line |
| 427 | + match_results(i) = match_line(line, patterns, opts, compiled) |
| 428 | + end do |
| 429 | + |
| 430 | + end subroutine match_lines_batch |
| 431 | + |
| 432 | + function can_use_batch_mode(src, opts) result(can_batch) |
| 433 | + !> Check if we can use optimized batch processing |
| 434 | + !> Batch mode works for simple cases without context lines or special modes |
| 435 | + type(input_source), intent(in) :: src |
| 436 | + type(grep_options), intent(in) :: opts |
| 437 | + logical :: can_batch |
| 438 | + |
| 439 | + can_batch = .false. |
| 440 | + |
| 441 | + ! Must be mmap source (has the file in memory) |
| 442 | + if (src%source_type /= SOURCE_MMAP) return |
| 443 | + |
| 444 | + ! Can't use batch with context lines |
| 445 | + if (opts%before_context > 0 .or. opts%after_context > 0) return |
| 446 | + |
| 447 | + ! Can't use batch with invert match (need careful line tracking) |
| 448 | + if (opts%invert_match) return |
| 449 | + |
| 450 | + ! Can't use batch with only-matching mode |
| 451 | + if (opts%only_matching) return |
| 452 | + |
| 453 | + ! Can't use batch with files-without-match |
| 454 | + if (opts%files_without_match) return |
| 455 | + |
| 456 | + ! Can't use batch with null-data mode |
| 457 | + if (opts%null_data) return |
| 458 | + |
| 459 | + can_batch = .true. |
| 460 | + |
| 461 | + end function can_use_batch_mode |
| 462 | + |
| 463 | + function process_source_batch(src, patterns, opts, compiled) result(found_match) |
| 464 | + !> Process a source using batch mode for improved performance |
| 465 | + !> This is a fast path for simple search modes |
| 466 | + type(input_source), intent(inout) :: src |
| 467 | + character(len=max_pattern_len), intent(in) :: patterns(:) |
| 468 | + type(grep_options), intent(inout) :: opts |
| 469 | + type(compiled_patterns_t), intent(inout) :: compiled |
| 470 | + logical :: found_match |
| 471 | + |
| 472 | + type(line_batch_t) :: batch |
| 473 | + logical :: match_results(BATCH_SIZE) |
| 474 | + character(len=:), allocatable :: line |
| 475 | + integer :: i, match_count |
| 476 | + logical :: binary_matched |
| 477 | + |
| 478 | + ! For color mode |
| 479 | + integer, parameter :: MAX_MATCHES_PER_LINE = 100 |
| 480 | + integer :: match_starts(MAX_MATCHES_PER_LINE) |
| 481 | + integer :: match_ends(MAX_MATCHES_PER_LINE) |
| 482 | + integer :: num_matches |
| 483 | + |
| 484 | + found_match = .false. |
| 485 | + match_count = 0 |
| 486 | + binary_matched = .false. |
| 487 | + |
| 488 | + ! Process batches until EOF |
| 489 | + do while (src%read_lines_batch(batch)) |
| 490 | + ! Match all lines in batch |
| 491 | + call match_lines_batch(src, batch, patterns, opts, compiled, match_results) |
| 492 | + |
| 493 | + ! Process matches |
| 494 | + do i = 1, batch%count |
| 495 | + if (match_results(i)) then |
| 496 | + found_match = .true. |
| 497 | + match_count = match_count + 1 |
| 498 | + |
| 499 | + ! Handle binary files |
| 500 | + if (src%is_binary .and. .not. opts%text_mode) then |
| 501 | + if (.not. binary_matched) then |
| 502 | + call print_binary_match(src%filename, opts) |
| 503 | + binary_matched = .true. |
| 504 | + end if |
| 505 | + return |
| 506 | + end if |
| 507 | + |
| 508 | + ! Handle output modes |
| 509 | + if (opts%quiet) then |
| 510 | + return |
| 511 | + else if (opts%files_with_matches) then |
| 512 | + call print_filename(src%filename, opts) |
| 513 | + return |
| 514 | + else if (.not. opts%count_only) then |
| 515 | + ! Get line text and print it |
| 516 | + line = src%get_line_text(batch%lines(i)) |
| 517 | + if (opts%color_mode == COLOR_ALWAYS) then |
| 518 | + call find_matches(line, patterns, opts, compiled, match_starts, match_ends, num_matches) |
| 519 | + call print_match_colored(line, src%filename, batch%lines(i)%line_num, & |
| 520 | + batch%lines(i)%byte_off, opts, match_starts, match_ends, num_matches) |
| 521 | + else |
| 522 | + call print_match(line, src%filename, batch%lines(i)%line_num, & |
| 523 | + batch%lines(i)%byte_off, opts) |
| 524 | + end if |
| 525 | + end if |
| 526 | + |
| 527 | + ! Check max count |
| 528 | + if (opts%max_count > 0 .and. match_count >= opts%max_count) then |
| 529 | + if (opts%count_only) then |
| 530 | + call print_count(match_count, src%filename, opts) |
| 531 | + end if |
| 532 | + return |
| 533 | + end if |
| 534 | + end if |
| 535 | + end do |
| 536 | + end do |
| 537 | + |
| 538 | + ! Handle count mode |
| 539 | + if (opts%count_only) then |
| 540 | + call print_count(match_count, src%filename, opts) |
| 541 | + end if |
| 542 | + |
| 543 | + end function process_source_batch |
| 544 | + |
| 408 | 545 | subroutine find_matches(line, patterns, opts, compiled, match_starts, match_ends, num_matches) |
| 409 | 546 | !> Find all matches in a line, returning their positions |
| 410 | 547 | !> For -o mode, this finds all non-overlapping matches |
@@ -549,6 +686,12 @@ contains |
| 549 | 686 | logical :: use_context |
| 550 | 687 | integer :: k |
| 551 | 688 | |
| 689 | + ! Try optimized batch mode for simple cases |
| 690 | + if (present(compiled) .and. compiled%compiled .and. can_use_batch_mode(src, opts)) then |
| 691 | + found_match = process_source_batch(src, patterns, opts, compiled) |
| 692 | + return |
| 693 | + end if |
| 694 | + |
| 552 | 695 | found_match = .false. |
| 553 | 696 | match_count = 0 |
| 554 | 697 | binary_matched = .false. |
@@ -564,7 +707,7 @@ contains |
| 564 | 707 | allocate(before_buffer(opts%before_context)) |
| 565 | 708 | end if |
| 566 | 709 | |
| 567 | | - ! Process lines |
| 710 | + ! Process lines (line-by-line fallback) |
| 568 | 711 | do |
| 569 | 712 | ! Read next line with dynamic allocation (no length limit) |
| 570 | 713 | if (opts%null_data) then |