From 9a7accddb7a662034ecc94f9b921c671019dcc82 Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Sat, 7 Jan 2023 13:09:20 -0500 Subject: [PATCH] AK: Add an optional starting offset to `CircularBuffer::offset_of` This parameter allows to start searching after an offset. For example, to resume a search. It is unfortunately a breaking change in API so this patch also modifies one user and one test. --- AK/CircularBuffer.cpp | 22 +++++++++++----- AK/CircularBuffer.h | 2 +- Tests/AK/TestCircularBuffer.cpp | 40 ++++++++++++++++++++++++++++- Userland/Libraries/LibCore/Stream.h | 2 +- 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/AK/CircularBuffer.cpp b/AK/CircularBuffer.cpp index 2d8b279e6bd..7e38361a847 100644 --- a/AK/CircularBuffer.cpp +++ b/AK/CircularBuffer.cpp @@ -52,19 +52,29 @@ bool CircularBuffer::is_wrapping_around() const return capacity() <= m_reading_head + m_used_space; } -Optional CircularBuffer::offset_of(StringView needle, Optional until) const +Optional CircularBuffer::offset_of(StringView needle, Optional from, Optional until) const { + auto const read_from = from.value_or(0); auto const read_until = until.value_or(m_used_space); + VERIFY(read_from <= read_until); Array spans {}; spans[0] = next_read_span(); - if (spans[0].size() > read_until) - spans[0] = spans[0].trim(read_until); - else if (is_wrapping_around()) - spans[1] = m_buffer.span().slice(0, read_until - spans[0].size()); + if (read_from > 0) + spans[0] = spans[0].slice(min(spans[0].size(), read_from)); - return AK::memmem(spans.begin(), spans.end(), needle.bytes()); + if (spans[0].size() + read_from > read_until) + spans[0] = spans[0].trim(read_until - read_from); + + if (is_wrapping_around()) + spans[1] = m_buffer.span().slice(max(spans[0].size(), read_from) - spans[0].size(), min(read_until, m_used_space) - spans[0].size()); + + auto maybe_found = AK::memmem(spans.begin(), spans.end(), needle.bytes()); + if (maybe_found.has_value()) + *maybe_found += read_from; + + return maybe_found; } void CircularBuffer::clear() diff --git a/AK/CircularBuffer.h b/AK/CircularBuffer.h index 56763601c48..7b5e7a1f193 100644 --- a/AK/CircularBuffer.h +++ b/AK/CircularBuffer.h @@ -36,7 +36,7 @@ public: [[nodiscard]] size_t used_space() const; [[nodiscard]] size_t capacity() const; - Optional offset_of(StringView needle, Optional until = {}) const; + Optional offset_of(StringView needle, Optional from = {}, Optional until = {}) const; void clear(); diff --git a/Tests/AK/TestCircularBuffer.cpp b/Tests/AK/TestCircularBuffer.cpp index 6b279d51d41..8bab0233c24 100644 --- a/Tests/AK/TestCircularBuffer.cpp +++ b/Tests/AK/TestCircularBuffer.cpp @@ -272,6 +272,44 @@ TEST_CASE(offset_of) EXPECT(result.has_value()); EXPECT_EQ(result.value(), 13ul); - result = circular_buffer.offset_of("!Well"sv, 12); + result = circular_buffer.offset_of("!Well"sv, {}, 12); EXPECT(!result.has_value()); + + result = circular_buffer.offset_of("e"sv, 2); + EXPECT(result.has_value()); + EXPECT_EQ(result.value(), 9ul); +} + +TEST_CASE(offset_of_with_until_and_after) +{ + auto const source = "Well Hello Friends!"sv; + auto byte_buffer_or_error = ByteBuffer::copy(source.bytes()); + EXPECT(!byte_buffer_or_error.is_error()); + auto byte_buffer = byte_buffer_or_error.release_value(); + + auto circular_buffer_or_error = CircularBuffer::create_initialized(byte_buffer); + EXPECT(!circular_buffer_or_error.is_error()); + auto circular_buffer = circular_buffer_or_error.release_value(); + + auto result = circular_buffer.offset_of("Well Hello Friends!"sv, 0, 19); + EXPECT_EQ(result.value_or(42), 0ul); + + result = circular_buffer.offset_of(" Hello"sv, 4, 10); + EXPECT_EQ(result.value_or(42), 4ul); + + result = circular_buffer.offset_of("el"sv, 3, 10); + EXPECT_EQ(result.value_or(42), 6ul); + + safe_discard(circular_buffer, 5); + auto written_bytes = circular_buffer.write(byte_buffer.span().trim(5)); + EXPECT_EQ(written_bytes, 5ul); + + result = circular_buffer.offset_of("Hello Friends!Well "sv, 0, 19); + EXPECT_EQ(result.value_or(42), 0ul); + + result = circular_buffer.offset_of("o Frie"sv, 4, 10); + EXPECT_EQ(result.value_or(42), 4ul); + + result = circular_buffer.offset_of("el"sv, 3, 14); + EXPECT_EQ(result.value_or(42), 15ul); } diff --git a/Userland/Libraries/LibCore/Stream.h b/Userland/Libraries/LibCore/Stream.h index 2023cc304ed..6def6509089 100644 --- a/Userland/Libraries/LibCore/Stream.h +++ b/Userland/Libraries/LibCore/Stream.h @@ -719,7 +719,7 @@ public: Optional longest_match; size_t match_size = 0; for (auto& candidate : candidates) { - auto const result = m_buffer.offset_of(candidate, readable_size); + auto const result = m_buffer.offset_of(candidate, {}, readable_size); if (result.has_value()) { auto previous_match = longest_match.value_or(*result); if ((previous_match < *result) || (previous_match == *result && match_size < candidate.length())) {