1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 06:04:57 +00:00

LibTLS: Add segmentation to the application buffer to avoid memcpy churn

We were previously doing a *lot* of unnecessary memcpy work when
transferring large files.

This patch addresses the issue by introducing a simple segmented buffer
with no additional work when appending new data, or when transfering out
of the buffer.
This commit is contained in:
Andreas Kling 2024-01-03 10:53:51 +01:00
parent 40f87f0954
commit 27a294547d
3 changed files with 42 additions and 4 deletions

View file

@ -498,7 +498,7 @@ ssize_t TLSv12::handle_message(ReadonlyBytes buffer)
} else {
dbgln_if(TLS_DEBUG, "application data message of size {}", plain.size());
if (m_context.application_buffer.try_append(plain.data(), plain.size()).is_error()) {
if (m_context.application_buffer.try_append(plain).is_error()) {
payload_res = (i8)Error::DecryptionFailed;
auto packet = build_alert(true, (u8)AlertDescription::DECRYPTION_FAILED_RESERVED);
write_packet(packet);

View file

@ -27,8 +27,7 @@ ErrorOr<Bytes> TLSv12::read_some(Bytes bytes)
return Bytes {};
}
TRY(m_context.application_buffer.slice(0, size_to_read)).span().copy_to(bytes);
m_context.application_buffer = TRY(m_context.application_buffer.slice(size_to_read, m_context.application_buffer.size() - size_to_read));
m_context.application_buffer.transfer(bytes, size_to_read);
return Bytes { bytes.data(), size_to_read };
}

View file

@ -8,6 +8,7 @@
#include "Certificate.h"
#include <AK/IPv4Address.h>
#include <AK/Queue.h>
#include <AK/WeakPtr.h>
#include <LibCore/Notifier.h>
#include <LibCore/Socket.h>
@ -197,6 +198,44 @@ struct Options {
#undef OPTION_WITH_DEFAULTS
};
class SegmentedBuffer {
public:
[[nodiscard]] size_t size() const { return m_size; }
[[nodiscard]] bool is_empty() const { return m_size == 0; }
void transfer(Bytes dest, size_t size)
{
VERIFY(size <= dest.size());
size_t transferred = 0;
while (transferred < size) {
auto& buffer = m_buffers.head();
size_t to_transfer = min(buffer.size() - m_offset_into_current_buffer, size - transferred);
memcpy(dest.offset(transferred), buffer.data() + m_offset_into_current_buffer, to_transfer);
transferred += to_transfer;
m_offset_into_current_buffer += to_transfer;
if (m_offset_into_current_buffer >= buffer.size()) {
m_buffers.dequeue();
m_offset_into_current_buffer = 0;
}
m_size -= to_transfer;
}
}
AK::ErrorOr<void> try_append(ReadonlyBytes data)
{
if (Checked<size_t>::addition_would_overflow(m_size, data.size()))
return AK::Error::from_errno(EOVERFLOW);
m_size += data.size();
m_buffers.enqueue(TRY(ByteBuffer::copy(data)));
return {};
}
private:
size_t m_size { 0 };
Queue<ByteBuffer> m_buffers;
size_t m_offset_into_current_buffer { 0 };
};
struct Context {
bool verify_chain(StringView host) const;
bool verify_certificate_pair(Certificate const& subject, Certificate const& issuer) const;
@ -237,7 +276,7 @@ struct Context {
ByteBuffer tls_buffer;
ByteBuffer application_buffer;
SegmentedBuffer application_buffer;
bool is_child { false };