diff --git a/AK/CircularBuffer.cpp b/AK/CircularBuffer.cpp index becef3c58fc..364e8da1094 100644 --- a/AK/CircularBuffer.cpp +++ b/AK/CircularBuffer.cpp @@ -220,7 +220,13 @@ ErrorOr CircularBuffer::copy_from_seekback(size_t distance, size_t lengt if (next_span.size() == 0) break; - remaining_length -= write(next_span.trim(remaining_length)); + auto length_written = write(next_span.trim(remaining_length)); + remaining_length -= length_written; + + // If we copied right from the end of the seekback area (i.e. our length is larger than the distance) + // and the last copy was one complete "chunk", we can now double the distance to copy twice as much data in one go. + if (remaining_length > distance && length_written == distance) + distance *= 2; } return length - remaining_length; diff --git a/Tests/AK/TestCircularBuffer.cpp b/Tests/AK/TestCircularBuffer.cpp index 5551d72d3ca..309bfbf2979 100644 --- a/Tests/AK/TestCircularBuffer.cpp +++ b/Tests/AK/TestCircularBuffer.cpp @@ -348,3 +348,18 @@ TEST_CASE(offset_of_with_until_and_after_wrapping_around) result = circular_buffer.offset_of("Well "sv, 14, 19); EXPECT_EQ(result.value_or(42), 14ul); } + +BENCHMARK_CASE(looping_copy_from_seekback) +{ + auto circular_buffer = MUST(CircularBuffer::create_empty(16 * MiB)); + + { + auto written_bytes = circular_buffer.write("\0"sv.bytes()); + EXPECT_EQ(written_bytes, 1ul); + } + + { + auto copied_bytes = TRY_OR_FAIL(circular_buffer.copy_from_seekback(1, 15 * MiB)); + EXPECT_EQ(copied_bytes, 15 * MiB); + } +}