diff --git a/AK/BufferedStream.h b/AK/BufferedStream.h index 6b618e17c6..2c1450d119 100644 --- a/AK/BufferedStream.h +++ b/AK/BufferedStream.h @@ -175,15 +175,21 @@ public: return Optional {}; } - // Returns whether a line can be read, populating the buffer in the process. - ErrorOr can_read_line() + // Populates the buffer, and returns whether it is possible to read up to the given delimiter. + ErrorOr can_read_up_to_delimiter(ReadonlyBytes delimiter) { if (stream().is_eof()) - return m_buffer.used_space() > 0; + return m_buffer.offset_of(delimiter).has_value(); - auto maybe_match = TRY(find_and_populate_until_any_of(Array { "\n"sv })); + auto maybe_match = TRY(find_and_populate_until_any_of(Array { StringView { delimiter } })); if (maybe_match.has_value()) return true; + + return stream().is_eof() && m_buffer.offset_of(delimiter).has_value(); + } + + bool is_eof_with_data_left_over() const + { return stream().is_eof() && m_buffer.used_space() > 0; } @@ -292,10 +298,14 @@ public: } ErrorOr read_line(Bytes buffer) { return m_helper.read_line(move(buffer)); } + ErrorOr can_read_line() + { + return TRY(m_helper.can_read_up_to_delimiter("\n"sv.bytes())) || m_helper.is_eof_with_data_left_over(); + } ErrorOr read_until(Bytes buffer, StringView candidate) { return m_helper.read_until(move(buffer), move(candidate)); } template ErrorOr read_until_any_of(Bytes buffer, Array candidates) { return m_helper.read_until_any_of(move(buffer), move(candidates)); } - ErrorOr can_read_line() { return m_helper.can_read_line(); } + ErrorOr can_read_up_to_delimiter(ReadonlyBytes delimiter) { return m_helper.can_read_up_to_delimiter(delimiter); } size_t buffer_size() const { return m_helper.buffer_size(); } diff --git a/Tests/LibCore/TestLibCoreStream.cpp b/Tests/LibCore/TestLibCoreStream.cpp index 2468301a7d..490ccf3a56 100644 --- a/Tests/LibCore/TestLibCoreStream.cpp +++ b/Tests/LibCore/TestLibCoreStream.cpp @@ -532,6 +532,8 @@ TEST_CASE(buffered_file_without_newlines) auto can_read_line = TRY_OR_FAIL(ro_file->can_read_line()); EXPECT(can_read_line); + auto can_read_up_to_newline = TRY_OR_FAIL(ro_file->can_read_up_to_delimiter("\n"sv.bytes())); + EXPECT(!can_read_up_to_newline); Array buffer; EXPECT(ro_file->read_line(buffer).release_value() == new_newlines_message); } diff --git a/Userland/Libraries/LibCore/Socket.h b/Userland/Libraries/LibCore/Socket.h index c9b4622e17..e57abf7690 100644 --- a/Userland/Libraries/LibCore/Socket.h +++ b/Userland/Libraries/LibCore/Socket.h @@ -369,6 +369,7 @@ public: virtual ErrorOr read_line(Bytes buffer) = 0; virtual ErrorOr read_until(Bytes buffer, StringView candidate) = 0; virtual ErrorOr can_read_line() = 0; + virtual ErrorOr can_read_up_to_delimiter(ReadonlyBytes delimiter) = 0; virtual size_t buffer_size() const = 0; }; @@ -413,10 +414,14 @@ public: virtual void set_notifications_enabled(bool enabled) override { m_helper.stream().set_notifications_enabled(enabled); } virtual ErrorOr read_line(Bytes buffer) override { return m_helper.read_line(move(buffer)); } + virtual ErrorOr can_read_line() override + { + return TRY(m_helper.can_read_up_to_delimiter("\n"sv.bytes())) || m_helper.is_eof_with_data_left_over(); + } virtual ErrorOr read_until(Bytes buffer, StringView candidate) override { return m_helper.read_until(move(buffer), move(candidate)); } template ErrorOr read_until_any_of(Bytes buffer, Array candidates) { return m_helper.read_until_any_of(move(buffer), move(candidates)); } - virtual ErrorOr can_read_line() override { return m_helper.can_read_line(); } + virtual ErrorOr can_read_up_to_delimiter(ReadonlyBytes delimiter) override { return m_helper.can_read_up_to_delimiter(delimiter); } virtual size_t buffer_size() const override { return m_helper.buffer_size(); }