@@ -405,6 +405,143 @@ contains |
| 405 | | 405 | |
| 406 | end function to_lower | 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 | subroutine find_matches(line, patterns, opts, compiled, match_starts, match_ends, num_matches) | 545 | subroutine find_matches(line, patterns, opts, compiled, match_starts, match_ends, num_matches) |
| 409 | !> Find all matches in a line, returning their positions | 546 | !> Find all matches in a line, returning their positions |
| 410 | !> For -o mode, this finds all non-overlapping matches | 547 | !> For -o mode, this finds all non-overlapping matches |
@@ -549,6 +686,12 @@ contains |
| 549 | logical :: use_context | 686 | logical :: use_context |
| 550 | integer :: k | 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 | found_match = .false. | 695 | found_match = .false. |
| 553 | match_count = 0 | 696 | match_count = 0 |
| 554 | binary_matched = .false. | 697 | binary_matched = .false. |
@@ -564,7 +707,7 @@ contains |
| 564 | allocate(before_buffer(opts%before_context)) | 707 | allocate(before_buffer(opts%before_context)) |
| 565 | end if | 708 | end if |
| 566 | | 709 | |
| 567 | - ! Process lines | 710 | + ! Process lines (line-by-line fallback) |
| 568 | do | 711 | do |
| 569 | ! Read next line with dynamic allocation (no length limit) | 712 | ! Read next line with dynamic allocation (no length limit) |
| 570 | if (opts%null_data) then | 713 | if (opts%null_data) then |