mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 11:07:45 +00:00
LibCore: Merge two search implementations in Stream::BufferedStream
`can_read_line()` and `read_until_any_of` used to have two different implementations to search for a needle. This commit factorizes both algorithms.
This commit is contained in:
parent
9a7accddb7
commit
b21ea54af0
1 changed files with 66 additions and 47 deletions
|
@ -694,6 +694,8 @@ public:
|
||||||
if (!TRY(can_read_line()))
|
if (!TRY(can_read_line()))
|
||||||
return Bytes {};
|
return Bytes {};
|
||||||
|
|
||||||
|
auto const candidate = TRY(find_and_populate_until_any_of(candidates, buffer.size()));
|
||||||
|
|
||||||
if (stream().is_eof()) {
|
if (stream().is_eof()) {
|
||||||
if (buffer.size() < m_buffer.used_space()) {
|
if (buffer.size() < m_buffer.used_space()) {
|
||||||
// Normally, reading from an EOFed stream and receiving bytes
|
// Normally, reading from an EOFed stream and receiving bytes
|
||||||
|
@ -710,27 +712,9 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const readable_size = min(m_buffer.used_space(), buffer.size());
|
if (candidate.has_value()) {
|
||||||
|
auto const read_bytes = m_buffer.read(buffer.trim(candidate->offset));
|
||||||
// The intention here is to try to match all of the possible
|
TRY(m_buffer.discard(candidate->size));
|
||||||
// delimiter candidates and try to find the longest one we can
|
|
||||||
// remove from the buffer after copying up to the delimiter to the
|
|
||||||
// user buffer.
|
|
||||||
Optional<size_t> longest_match;
|
|
||||||
size_t match_size = 0;
|
|
||||||
for (auto& candidate : candidates) {
|
|
||||||
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())) {
|
|
||||||
longest_match = result;
|
|
||||||
match_size = candidate.length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (longest_match.has_value()) {
|
|
||||||
auto const read_bytes = m_buffer.read(buffer.trim(*longest_match));
|
|
||||||
TRY(m_buffer.discard(match_size));
|
|
||||||
return read_bytes;
|
return read_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,37 +724,72 @@ public:
|
||||||
return m_buffer.read(buffer);
|
return m_buffer.read(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Match {
|
||||||
|
size_t offset {};
|
||||||
|
size_t size {};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t N>
|
||||||
|
ErrorOr<Optional<Match>> find_and_populate_until_any_of(Array<StringView, N> const& candidates, Optional<size_t> max_offset = {})
|
||||||
|
{
|
||||||
|
Optional<size_t> longest_candidate;
|
||||||
|
for (auto& candidate : candidates) {
|
||||||
|
if (candidate.length() >= longest_candidate.value_or(candidate.length()))
|
||||||
|
longest_candidate = candidate.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The intention here is to try to match all the possible
|
||||||
|
// delimiter candidates and try to find the longest one we can
|
||||||
|
// remove from the buffer after copying up to the delimiter to the
|
||||||
|
// user buffer.
|
||||||
|
|
||||||
|
auto const find_candidates = [this, &candidates, &longest_candidate](Optional<size_t> max_offset = {}) -> Optional<Match> {
|
||||||
|
max_offset = max_offset.value_or(m_buffer.used_space());
|
||||||
|
|
||||||
|
Optional<size_t> longest_match;
|
||||||
|
size_t match_size = 0;
|
||||||
|
for (auto& candidate : candidates) {
|
||||||
|
// FIXME: This currently searches through the buffer from the start,
|
||||||
|
// even if we just appended a small number of bytes at the end.
|
||||||
|
auto const result = m_buffer.offset_of(candidate, {}, *max_offset);
|
||||||
|
|
||||||
|
if (result.has_value()) {
|
||||||
|
auto previous_match = longest_match.value_or(*result);
|
||||||
|
if ((previous_match < *result) || (previous_match == *result && match_size < candidate.length())) {
|
||||||
|
longest_match = result;
|
||||||
|
match_size = candidate.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (longest_match.has_value())
|
||||||
|
return Match { *longest_match, match_size };
|
||||||
|
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto first_find = find_candidates(max_offset); first_find.has_value())
|
||||||
|
return first_find;
|
||||||
|
|
||||||
|
while (m_buffer.used_space() < max_offset.value_or(m_buffer.capacity())) {
|
||||||
|
auto const read_bytes = TRY(populate_read_buffer());
|
||||||
|
if (read_bytes == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (auto first_find = find_candidates(max_offset); first_find.has_value())
|
||||||
|
return first_find;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional<Match> {};
|
||||||
|
}
|
||||||
|
|
||||||
// Returns whether a line can be read, populating the buffer in the process.
|
// Returns whether a line can be read, populating the buffer in the process.
|
||||||
ErrorOr<bool> can_read_line()
|
ErrorOr<bool> can_read_line()
|
||||||
{
|
{
|
||||||
if (stream().is_eof() && m_buffer.used_space() > 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (m_buffer.offset_of("\n"sv).has_value())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (stream().is_eof())
|
if (stream().is_eof())
|
||||||
return false;
|
return m_buffer.used_space() > 0;
|
||||||
|
|
||||||
while (m_buffer.empty_space() > 0) {
|
return TRY(find_and_populate_until_any_of(Array<StringView, 1> { "\n"sv })).has_value();
|
||||||
auto populated_byte_count = TRY(populate_read_buffer());
|
|
||||||
|
|
||||||
if (stream().is_eof()) {
|
|
||||||
// We give the user one last hurrah to read the remaining
|
|
||||||
// contents as a "line".
|
|
||||||
return m_buffer.used_space() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: This currently searches through the buffer from the start,
|
|
||||||
// even if we just appended a small number of bytes at the end.
|
|
||||||
if (m_buffer.offset_of("\n"sv).has_value())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (populated_byte_count == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_eof() const
|
bool is_eof() const
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue