From 408ba6a46e42a74297188c2933f9c0b3aa31a3bf Mon Sep 17 00:00:00 2001 From: MsfPablo Date: Tue, 18 Aug 2026 12:57:51 +0200 Subject: [PATCH] grep: keep only the requested number of before-context lines --- src/context_buffer.rs | 10 +++++++--- tests/test_grep.rs | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/context_buffer.rs b/src/context_buffer.rs index 76ea5db..6d5981a 100644 --- a/src/context_buffer.rs +++ b/src/context_buffer.rs @@ -42,13 +42,16 @@ impl BufferedLine { /// provide a more optimized `ContextBuffer` when `mmap()` is available. pub struct ContextBuffer { slots: Vec, + /// Number of lines to retain. `slots` is rounded up to a power of two so + /// `push` can mask instead of divide, so it may be larger than this. + capacity: usize, head: usize, len: usize, } impl ContextBuffer { pub fn new(capacity: usize) -> Self { - let len = if capacity == 0 { + let slots = if capacity == 0 { 0 } else { capacity.next_power_of_two() @@ -60,8 +63,9 @@ impl ContextBuffer { line_number: 0, byte_offset: 0, }; - len + slots ], + capacity, head: 0, len: 0, } @@ -90,7 +94,7 @@ impl ContextBuffer { slot.byte_offset = byte_offset; self.head = self.head.wrapping_add(1); - self.len = (self.len + 1).min(self.slots.len()); + self.len = (self.len + 1).min(self.capacity); } pub fn drain_iter(&mut self) -> impl Iterator { diff --git a/tests/test_grep.rs b/tests/test_grep.rs index b477329..3303fc6 100644 --- a/tests/test_grep.rs +++ b/tests/test_grep.rs @@ -1050,6 +1050,26 @@ fn after_before_combined_context() { .stdout_only("b\nMATCH\nc\n"); } +#[test] +fn before_context_is_not_rounded_to_power_of_two() { + // The context ring buffer rounds its slot count up to a power of two, but + // must still retain only the requested number of lines. + let input = "01\n02\n03\n04\n05\n06\n07\n08\n09\n10\nMM\n"; + + for (n, expected) in [ + ("3", "08\n09\n10\nMM\n"), + ("5", "06\n07\n08\n09\n10\nMM\n"), + ("6", "05\n06\n07\n08\n09\n10\nMM\n"), + ("7", "04\n05\n06\n07\n08\n09\n10\nMM\n"), + ] { + let (_s, mut c) = ucmd(); + c.args(&["-e", "MM", "-B", n]) + .pipe_in(input) + .succeeds() + .stdout_only(expected); + } +} + #[test] fn num_shorthand_is_context() { // `-2` is shorthand for `-C 2`.