fix: revert buffer reserve space (#5528)

* Revert "refactor: use BufferReader, BufferWriter as function args (#5518)"

This reverts commit c2d48a7d11.

* Revert "fixup! refactor: add BufferReader, BufferWriter::reserve_space() (#5513)"

This reverts commit b08e17beef.

* Revert "refactor: add BufferReader, BufferWriter::reserve_space() (#5513)"

This reverts commit 51fd7056ba.
This commit is contained in:
Charles Kerr 2023-05-14 17:24:42 -05:00 committed by GitHub
parent e88bf946e1
commit 2cf9678737
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 436 additions and 292 deletions

View File

@ -49,8 +49,6 @@ using namespace std::literals;
using tau_connection_t = uint64_t;
using tau_transaction_t = uint32_t;
using InBuf = libtransmission::BufferReader<std::byte>;
constexpr auto TauConnectionTtlSecs = time_t{ 45 };
auto tau_transaction_new()
@ -116,7 +114,7 @@ struct tau_scrape_request
requestFinished();
}
void onResponse(tau_action_t action, InBuf& buf)
void onResponse(tau_action_t action, libtransmission::Buffer& buf)
{
response.did_connect = true;
response.did_timeout = false;
@ -216,7 +214,7 @@ struct tau_announce_request
this->requestFinished();
}
void onResponse(tau_action_t action, InBuf& buf)
void onResponse(tau_action_t action, libtransmission::Buffer& buf)
{
auto const buflen = std::size(buf);
@ -229,7 +227,8 @@ struct tau_announce_request
response.leechers = buf.to_uint32();
response.seeders = buf.to_uint32();
response.pex = tr_pex::from_compact_ipv4(std::data(buf), std::size(buf), nullptr, 0);
auto const [bytes, n_bytes] = buf.pullup();
response.pex = tr_pex::from_compact_ipv4(bytes, n_bytes, nullptr, 0);
requestFinished();
}
else
@ -311,7 +310,7 @@ struct tau_tracker
mediator_.sendto(buf, buflen, reinterpret_cast<sockaddr const*>(&ss), sslen);
}
void on_connection_response(tau_action_t action, InBuf& buf)
void on_connection_response(tau_action_t action, libtransmission::Buffer& buf)
{
this->connecting_at = 0;
this->connection_transaction_id = 0;
@ -379,7 +378,8 @@ struct tau_tracker
buf.add_uint32(TAU_ACTION_CONNECT);
buf.add_uint32(this->connection_transaction_id);
this->sendto(std::data(buf), std::size(buf));
auto const [bytes, n_bytes] = buf.pullup();
this->sendto(bytes, n_bytes);
}
if (timeout_reqs)
@ -539,7 +539,8 @@ private:
buf.add_uint64(this->connection_id);
buf.add(payload, payload_len);
this->sendto(std::data(buf), std::size(buf));
auto const [bytes, n_bytes] = buf.pullup();
this->sendto(bytes, n_bytes);
}
public:

View File

@ -575,6 +575,30 @@ size_t tr_peerIo::get_write_buffer_space(uint64_t now) const noexcept
return desired_len > current_len ? desired_len - current_len : 0U;
}
void tr_peerIo::write(libtransmission::Buffer& buf, bool is_piece_data)
{
auto [bytes, len] = buf.pullup();
encrypt(len, bytes);
outbuf_info_.emplace_back(std::size(buf), is_piece_data);
outbuf_.add(buf);
buf.clear();
}
void tr_peerIo::write_bytes(void const* bytes, size_t n_bytes, bool is_piece_data)
{
auto const old_size = std::size(outbuf_);
outbuf_.reserve(old_size + n_bytes);
outbuf_.add(bytes, n_bytes);
for (auto iter = std::begin(outbuf_) + old_size, end = std::end(outbuf_); iter != end; ++iter)
{
encrypt(1, &*iter);
}
outbuf_info_.emplace_back(n_bytes, is_piece_data);
}
// ---
void tr_peerIo::read_bytes(void* bytes, size_t byte_count)

View File

@ -123,24 +123,11 @@ public:
[[nodiscard]] size_t get_write_buffer_space(uint64_t now) const noexcept;
template<typename T>
void write_bytes(T const* buf, size_t n_bytes, bool is_piece_data)
{
outbuf_info_.emplace_back(n_bytes, is_piece_data);
auto [resbuf, reslen] = outbuf_.reserve_space(n_bytes);
filter_.encrypt(reinterpret_cast<std::byte const*>(buf), resbuf, n_bytes);
outbuf_.commit_space(n_bytes);
}
void write_bytes(void const* bytes, size_t n_bytes, bool is_piece_data);
// Write all the data from `buf`.
// This is a destructive add: `buf` is empty after this call.
void write(libtransmission::BufferReader<std::byte>& buf, bool is_piece_data)
{
auto const n_bytes = std::size(buf);
write_bytes(std::data(buf), n_bytes, is_piece_data);
buf.drain(n_bytes);
}
void write(libtransmission::Buffer& buf, bool is_piece_data);
size_t flush_outgoing_protocol_msgs();

View File

@ -11,7 +11,6 @@
#error only libtransmission should #include this header.
#endif
#include <algorithm> // for std::copy_n
#include <array>
#include <cstddef> // size_t, std::byte
#include <memory>
@ -100,19 +99,6 @@ public:
}
}
template<typename T>
constexpr void encrypt(T const* src_data, T* dst_data, size_t n_bytes)
{
if (enc_active_)
{
enc_key_.process(src_data, dst_data, n_bytes);
}
else
{
std::copy_n(src_data, n_bytes, dst_data);
}
}
[[nodiscard]] constexpr auto is_active() const noexcept
{
return dec_active_ || enc_active_;

View File

@ -47,8 +47,7 @@
#endif
using namespace std::literals;
using PeerMessageBuffer = libtransmission::Buffer;
using PeerMessageReader = libtransmission::BufferReader<std::byte>;
using Buffer = libtransmission::Buffer;
namespace
{
@ -219,7 +218,7 @@ struct tr_incoming
{
std::optional<uint32_t> length; // the full message payload length. Includes the +1 for id length
std::optional<uint8_t> id; // the protocol message, e.g. BtPeerMsgs::Piece
PeerMessageBuffer payload;
Buffer payload;
struct incoming_piece_data
{
@ -678,8 +677,8 @@ public:
tr_bitfield have_;
private:
friend ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, PeerMessageReader& payload);
friend void parseLtepHandshake(tr_peerMsgsImpl* msgs, PeerMessageReader& payload);
friend ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmission::Buffer& payload);
friend void parseLtepHandshake(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload);
tr_peer_callback const callback_;
void* const callback_data_;
@ -758,26 +757,23 @@ template<typename T>
// ---
template<typename BufferWriter>
void add_param(BufferWriter& buffer, uint8_t param) noexcept
void add_param(Buffer& buffer, uint8_t param) noexcept
{
buffer.add_uint8(param);
}
template<typename BufferWriter>
void add_param(BufferWriter& buffer, uint16_t param) noexcept
void add_param(Buffer& buffer, uint16_t param) noexcept
{
buffer.add_uint16(param);
}
template<typename BufferWriter>
void add_param(BufferWriter& buffer, uint32_t param) noexcept
void add_param(Buffer& buffer, uint32_t param) noexcept
{
buffer.add_uint32(param);
}
template<typename BufferWriter, typename T>
void add_param(BufferWriter& buffer, T const& param) noexcept
template<typename T>
void add_param(Buffer& buffer, T const& param) noexcept
{
buffer.add(param);
}
@ -814,17 +810,20 @@ template<typename... Args>
}
} // namespace
template<typename BufferWriter, typename... Args>
void build_peer_message(tr_peerMsgsImpl const* const msgs, BufferWriter& out, uint8_t type, Args const&... args)
template<typename... Args>
void build_peer_message(tr_peerMsgsImpl const* const msgs, Buffer& out, uint8_t type, Args const&... args)
{
logtrace(msgs, build_log_message(type, args...));
auto const old_len = std::size(out);
auto msg_len = sizeof(type);
((msg_len += get_param_length(args)), ...);
out.reserve(old_len + msg_len);
out.add_uint32(msg_len);
out.add_uint8(type);
(add_param(out, args), ...);
TR_ASSERT(old_len + sizeof(uint32_t) + msg_len);
TR_ASSERT(messageLengthIsCorrect(msgs->torrent, type, msg_len));
}
} // namespace protocol_send_message_helpers
@ -834,7 +833,7 @@ size_t protocol_send_message(tr_peerMsgsImpl const* const msgs, uint8_t type, Ar
{
using namespace protocol_send_message_helpers;
auto out = PeerMessageBuffer{};
auto out = Buffer{};
build_peer_message(msgs, out, type, args...);
auto const n_bytes_added = std::size(out);
msgs->io->write(out, type == BtPeerMsgs::Piece);
@ -845,7 +844,7 @@ size_t protocol_send_keepalive(tr_peerMsgsImpl* msgs)
{
logtrace(msgs, "sending 'keepalive'");
auto out = libtransmission::Buffer{};
auto out = Buffer{};
out.add_uint32(0);
auto const n_bytes_added = std::size(out);
@ -1036,11 +1035,11 @@ void sendLtepHandshake(tr_peerMsgsImpl* msgs)
tr_variantClear(&val);
}
void parseLtepHandshake(tr_peerMsgsImpl* msgs, PeerMessageReader& payload)
void parseLtepHandshake(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload)
{
msgs->peerSentLtepHandshake = true;
auto const handshake_sv = payload.to_string_view();
auto const handshake_sv = payload.pullup_sv();
auto val = tr_variant{};
if (!tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, handshake_sv) || !tr_variantIsDict(&val))
@ -1146,13 +1145,13 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, PeerMessageReader& payload)
tr_variantClear(&val);
}
void parseUtMetadata(tr_peerMsgsImpl* msgs, PeerMessageReader& payload_in)
void parseUtMetadata(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload_in)
{
int64_t msg_type = -1;
int64_t piece = -1;
int64_t total_size = 0;
auto const tmp = payload_in.to_string_view();
auto const tmp = payload_in.pullup_sv();
auto const* const msg_end = std::data(tmp) + std::size(tmp);
auto dict = tr_variant{};
@ -1201,7 +1200,7 @@ void parseUtMetadata(tr_peerMsgsImpl* msgs, PeerMessageReader& payload_in)
}
}
void parseUtPex(tr_peerMsgsImpl* msgs, PeerMessageReader& payload)
void parseUtPex(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload)
{
auto* const tor = msgs->torrent;
if (!tor->allows_pex())
@ -1209,7 +1208,7 @@ void parseUtPex(tr_peerMsgsImpl* msgs, PeerMessageReader& payload)
return;
}
auto const tmp = payload.to_string_view();
auto const tmp = payload.pullup_sv();
if (tr_variant val; tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, tmp))
{
@ -1249,7 +1248,7 @@ void parseUtPex(tr_peerMsgsImpl* msgs, PeerMessageReader& payload)
}
}
void parseLtep(tr_peerMsgsImpl* msgs, PeerMessageReader& payload)
void parseLtep(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload)
{
TR_ASSERT(!std::empty(payload));
@ -1284,7 +1283,7 @@ void parseLtep(tr_peerMsgsImpl* msgs, PeerMessageReader& payload)
}
}
ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, PeerMessageReader& payload);
ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmission::Buffer& payload);
void prefetchPieces(tr_peerMsgsImpl* msgs)
{
@ -1349,7 +1348,7 @@ void peerMadeRequest(tr_peerMsgsImpl* msgs, struct peer_request const* req)
int clientGotBlock(tr_peerMsgsImpl* msgs, std::unique_ptr<Cache::BlockData> block_data, tr_block_index_t block);
ReadResult read_piece_data(tr_peerMsgsImpl* msgs, PeerMessageReader& payload)
ReadResult read_piece_data(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload)
{
// <index><begin><block>
auto const piece = payload.to_uint32();
@ -1397,7 +1396,7 @@ ReadResult read_piece_data(tr_peerMsgsImpl* msgs, PeerMessageReader& payload)
return { ok ? READ_NOW : READ_ERR, len };
}
ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, PeerMessageReader& payload)
ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmission::Buffer& payload)
{
bool const fext = msgs->io->supports_fext();
@ -1478,12 +1477,15 @@ ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, PeerMessageRe
break;
case BtPeerMsgs::Bitfield:
logtrace(msgs, "got a bitfield");
msgs->have_ = tr_bitfield{ msgs->torrent->has_metainfo() ? msgs->torrent->piece_count() : std::size(payload) * 8 };
msgs->have_.set_raw(reinterpret_cast<uint8_t const*>(std::data(payload)), std::size(payload));
msgs->publish(tr_peer_event::GotBitfield(&msgs->have_));
msgs->invalidatePercentDone();
break;
{
logtrace(msgs, "got a bitfield");
auto const [buf, buflen] = payload.pullup();
msgs->have_ = tr_bitfield{ msgs->torrent->has_metainfo() ? msgs->torrent->piece_count() : buflen * 8 };
msgs->have_.set_raw(reinterpret_cast<uint8_t const*>(buf), buflen);
msgs->publish(tr_peer_event::GotBitfield(&msgs->have_));
msgs->invalidatePercentDone();
break;
}
case BtPeerMsgs::Request:
{

View File

@ -70,7 +70,7 @@ void tr_peer_socket::close()
handle = {};
}
size_t tr_peer_socket::try_write(OutBuf& buf, size_t max, tr_error** error) const
size_t tr_peer_socket::try_write(Buffer& buf, size_t max, tr_error** error) const
{
if (max == size_t{})
{
@ -85,9 +85,7 @@ size_t tr_peer_socket::try_write(OutBuf& buf, size_t max, tr_error** error) cons
#ifdef WITH_UTP
if (is_utp())
{
// NB: libutp doesn't change `data` but requires the arg to be non-const anyway
auto* const data = const_cast<std::byte*>(std::data(buf));
auto const datalen = std::size(buf);
auto const [data, datalen] = buf.pullup();
errno = 0;
auto const n_written = utp_write(handle.utp, data, std::min(datalen, max));

View File

@ -28,7 +28,6 @@ class tr_peer_socket
{
public:
using Buffer = libtransmission::Buffer;
using OutBuf = libtransmission::BufferReader<std::byte>;
tr_peer_socket() = default;
tr_peer_socket(tr_session const* session, tr_address const& address, tr_port port, tr_socket_t sock);
@ -57,8 +56,8 @@ public:
}
void close();
size_t try_write(Buffer& buf, size_t max, tr_error** error) const;
size_t try_read(Buffer& buf, size_t max, tr_error** error) const;
size_t try_write(OutBuf& buf, size_t max, tr_error** error) const;
[[nodiscard]] constexpr std::pair<tr_address, tr_port> socketAddress() const noexcept
{

View File

@ -5,18 +5,15 @@
#pragma once
#include <cstddef> // for std::byte
#include <cstddef>
#include <iterator>
#include <limits>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <event2/buffer.h>
#include <fmt/core.h>
#include "error.h"
#include "net.h" // tr_socket_t
#include "tr-assert.h"
@ -26,117 +23,21 @@
namespace libtransmission
{
template<typename value_type>
class BufferReader
{
public:
virtual ~BufferReader() = default;
virtual void drain(size_t n_bytes) = 0;
[[nodiscard]] virtual size_t size() const noexcept = 0;
[[nodiscard]] virtual value_type const* data() const = 0;
[[nodiscard]] auto empty() const noexcept
{
return size() == 0U;
}
[[nodiscard]] auto* begin() noexcept
{
return data();
}
[[nodiscard]] auto const* begin() const
{
return data();
}
[[nodiscard]] auto const* end() const
{
return begin() + size();
}
[[nodiscard]] auto to_string() const
{
return std::string{ reinterpret_cast<char const*>(data()), size() };
}
[[nodiscard]] auto to_string_view() const
{
return std::string_view{ reinterpret_cast<char const*>(data()), size() };
}
template<typename T>
[[nodiscard]] bool starts_with(T const& needle) const
{
auto const n_bytes = std::size(needle);
auto const needle_begin = reinterpret_cast<value_type const*>(std::data(needle));
auto const needle_end = needle_begin + n_bytes;
return n_bytes <= size() && std::equal(needle_begin, needle_end, data());
}
auto to_buf(void* tgt, size_t n_bytes)
{
n_bytes = std::min(n_bytes, size());
std::copy_n(data(), n_bytes, reinterpret_cast<value_type*>(tgt));
drain(n_bytes);
return n_bytes;
}
[[nodiscard]] auto to_uint8()
{
auto tmp = uint8_t{};
to_buf(&tmp, sizeof(tmp));
return tmp;
}
[[nodiscard]] uint16_t to_uint16()
{
auto tmp = uint16_t{};
to_buf(&tmp, sizeof(tmp));
return ntohs(tmp);
}
[[nodiscard]] uint32_t to_uint32()
{
auto tmp = uint32_t{};
to_buf(&tmp, sizeof(tmp));
return ntohl(tmp);
}
[[nodiscard]] uint64_t to_uint64()
{
auto tmp = uint64_t{};
to_buf(&tmp, sizeof(tmp));
return tr_ntohll(tmp);
}
size_t to_socket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr)
{
if (auto const n_sent = send(sockfd, reinterpret_cast<char const*>(data()), std::min(n_bytes, size()), 0); n_sent >= 0)
{
drain(n_sent);
return n_sent;
}
auto const err = sockerrno;
tr_error_set(error, err, tr_net_strerror(err));
return {};
}
};
template<typename value_type>
template<typename T, typename ValueType>
class BufferWriter
{
public:
virtual ~BufferWriter() = default;
virtual std::pair<value_type*, size_t> reserve_space(size_t n_bytes) = 0;
virtual void commit_space(size_t n_bytes) = 0;
BufferWriter(T* out)
: out_{ out }
{
static_assert(sizeof(ValueType) == 1);
}
void add(void const* span_begin, size_t span_len)
{
auto [buf, buflen] = reserve_space(span_len);
std::copy_n(reinterpret_cast<value_type const*>(span_begin), span_len, buf);
commit_space(span_len);
auto const* const begin = reinterpret_cast<ValueType const*>(span_begin);
auto const* const end = begin + span_len;
out_->insert(std::end(*out_), begin, end);
}
template<typename ContiguousContainer>
@ -195,76 +96,344 @@ public:
add(&nport, sizeof(nport));
}
size_t add_socket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr)
{
auto const [buf, buflen] = reserve_space(n_bytes);
if (auto const n_read = recv(sockfd, reinterpret_cast<char*>(buf), n_bytes, 0); n_read >= 0)
{
commit_space(n_read);
return n_read;
}
auto const err = sockerrno;
tr_error_set(error, err, tr_net_strerror(err));
return {};
}
private:
T* out_;
};
class Buffer final
: public BufferReader<std::byte>
, public BufferWriter<std::byte>
class Buffer : public BufferWriter<Buffer, std::byte>
{
public:
using value_type = std::byte;
class Iterator
{
public:
using difference_type = long;
using value_type = std::byte;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::random_access_iterator_tag;
constexpr Iterator(evbuffer* const buf, size_t offset)
: buf_{ buf }
, buf_offset_{ offset }
{
}
[[nodiscard]] value_type& operator*() noexcept
{
auto& info = iov();
return static_cast<value_type*>(info.iov.iov_base)[info.offset];
}
[[nodiscard]] value_type operator*() const noexcept
{
auto const& info = iov();
return static_cast<value_type*>(info.iov.iov_base)[info.offset];
}
[[nodiscard]] constexpr Iterator operator+(size_t n_bytes)
{
return Iterator{ buf_, offset() + n_bytes };
}
[[nodiscard]] constexpr Iterator operator-(size_t n_bytes)
{
return Iterator{ buf_, offset() - n_bytes };
}
[[nodiscard]] constexpr auto operator-(Iterator const& that) const noexcept
{
return offset() - that.offset();
}
constexpr Iterator& operator++() noexcept
{
inc_offset(1U);
return *this;
}
constexpr Iterator& operator+=(size_t n_bytes)
{
inc_offset(n_bytes);
return *this;
}
constexpr Iterator& operator--() noexcept
{
dec_offset(1);
return *this;
}
[[nodiscard]] constexpr bool operator==(Iterator const& that) const noexcept
{
return this->buf_ == that.buf_ && this->offset() == that.offset();
}
[[nodiscard]] constexpr bool operator!=(Iterator const& that) const noexcept
{
return !(*this == that);
}
private:
struct IovInfo
{
evbuffer_iovec iov = {};
size_t offset = 0;
};
[[nodiscard]] constexpr size_t offset() const noexcept
{
return buf_offset_;
}
constexpr void dec_offset(size_t increment)
{
buf_offset_ -= increment;
if (iov_)
{
if (iov_->offset >= increment)
{
iov_->offset -= increment;
}
else
{
iov_.reset();
}
}
}
constexpr void inc_offset(size_t increment)
{
buf_offset_ += increment;
if (iov_)
{
if (iov_->offset + increment < iov_->iov.iov_len)
{
iov_->offset += increment;
}
else
{
iov_.reset();
}
}
}
[[nodiscard]] IovInfo& iov() const noexcept
{
if (!iov_)
{
auto ptr = evbuffer_ptr{};
auto iov = IovInfo{};
evbuffer_ptr_set(buf_, &ptr, buf_offset_, EVBUFFER_PTR_SET);
evbuffer_peek(buf_, std::numeric_limits<ev_ssize_t>::max(), &ptr, &iov.iov, 1);
iov.offset = 0;
iov_ = iov;
}
return *iov_;
}
mutable std::optional<IovInfo> iov_;
evbuffer* buf_;
size_t buf_offset_ = 0;
};
Buffer()
: BufferWriter<Buffer, std::byte>{ this }
{
}
Buffer(Buffer&& that)
: BufferWriter<Buffer, std::byte>(this)
, buf_{ std::move(that.buf_) }
{
}
Buffer& operator=(Buffer&& that)
{
buf_ = std::move(that.buf_);
return *this;
}
Buffer() = default;
Buffer(Buffer&&) = default;
Buffer(Buffer const&) = delete;
Buffer& operator=(Buffer&&) = default;
Buffer& operator=(Buffer const&) = delete;
template<typename T>
explicit Buffer(T const& data)
: BufferWriter<Buffer, std::byte>{ this }
{
add(data);
}
[[nodiscard]] size_t size() const noexcept override
[[nodiscard]] auto size() const noexcept
{
return evbuffer_get_length(buf_.get());
}
[[nodiscard]] value_type const* data() const override
[[nodiscard]] auto empty() const noexcept
{
return reinterpret_cast<value_type*>(evbuffer_pullup(buf_.get(), -1));
return evbuffer_get_length(buf_.get()) == 0;
}
void drain(size_t n_bytes) override
[[nodiscard]] auto begin() noexcept
{
return Iterator{ buf_.get(), 0U };
}
[[nodiscard]] auto end() noexcept
{
return Iterator{ buf_.get(), size() };
}
[[nodiscard]] auto begin() const noexcept
{
return Iterator{ buf_.get(), 0U };
}
[[nodiscard]] auto end() const noexcept
{
return Iterator{ buf_.get(), size() };
}
template<typename T>
[[nodiscard]] TR_CONSTEXPR20 bool starts_with(T const& needle) const
{
auto const n_bytes = std::size(needle);
auto const needle_begin = reinterpret_cast<std::byte const*>(std::data(needle));
auto const needle_end = needle_begin + n_bytes;
return n_bytes <= size() && std::equal(needle_begin, needle_end, cbegin());
}
[[nodiscard]] std::string to_string() const
{
auto str = std::string{};
str.resize(size());
evbuffer_copyout(buf_.get(), std::data(str), std::size(str));
return str;
}
auto to_buf(void* tgt, size_t n_bytes)
{
return evbuffer_remove(buf_.get(), tgt, n_bytes);
}
[[nodiscard]] auto to_uint8()
{
auto tmp = uint8_t{};
to_buf(&tmp, sizeof(tmp));
return tmp;
}
[[nodiscard]] uint16_t to_uint16()
{
auto tmp = uint16_t{};
to_buf(&tmp, sizeof(tmp));
return ntohs(tmp);
}
[[nodiscard]] uint32_t to_uint32()
{
auto tmp = uint32_t{};
to_buf(&tmp, sizeof(tmp));
return ntohl(tmp);
}
[[nodiscard]] uint64_t to_uint64()
{
auto tmp = uint64_t{};
to_buf(&tmp, sizeof(tmp));
return tr_ntohll(tmp);
}
void drain(size_t n_bytes)
{
evbuffer_drain(buf_.get(), n_bytes);
}
virtual std::pair<value_type*, size_t> reserve_space(size_t n_bytes) override
void clear()
{
auto iov = evbuffer_iovec{};
evbuffer_reserve_space(buf_.get(), n_bytes, &iov, 1);
TR_ASSERT(iov.iov_len >= n_bytes);
reserved_space_ = iov;
return { static_cast<value_type*>(iov.iov_base), static_cast<size_t>(iov.iov_len) };
drain(size());
}
virtual void commit_space(size_t n_bytes) override
// Returns the number of bytes written. Check `error` for error.
size_t to_socket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr)
{
TR_ASSERT(reserved_space_);
TR_ASSERT(reserved_space_->iov_len >= n_bytes);
reserved_space_->iov_len = n_bytes;
evbuffer_commit_space(buf_.get(), &*reserved_space_, 1);
reserved_space_.reset();
EVUTIL_SET_SOCKET_ERROR(0);
auto const res = evbuffer_write_atmost(buf_.get(), sockfd, n_bytes);
auto const err = EVUTIL_SOCKET_ERROR();
if (res >= 0)
{
return static_cast<size_t>(res);
}
tr_error_set(error, err, tr_net_strerror(err));
return 0;
}
[[nodiscard]] std::pair<std::byte*, size_t> pullup()
{
return { reinterpret_cast<std::byte*>(evbuffer_pullup(buf_.get(), -1)), size() };
}
[[nodiscard]] std::byte const* data() const
{
return reinterpret_cast<std::byte*>(evbuffer_pullup(buf_.get(), -1));
}
[[nodiscard]] auto pullup_sv()
{
auto const [buf, buflen] = pullup();
return std::string_view{ reinterpret_cast<char const*>(buf), buflen };
}
void reserve(size_t n_bytes)
{
evbuffer_expand(buf_.get(), n_bytes - size());
}
size_t add_socket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr)
{
EVUTIL_SET_SOCKET_ERROR(0);
auto const res = evbuffer_read(buf_.get(), sockfd, static_cast<int>(n_bytes));
auto const err = EVUTIL_SOCKET_ERROR();
if (res > 0)
{
return static_cast<size_t>(res);
}
if (res == 0)
{
tr_error_set_from_errno(error, ENOTCONN);
}
else
{
tr_error_set(error, err, tr_net_strerror(err));
}
return {};
}
template<typename T>
void insert([[maybe_unused]] Iterator iter, T const* const begin, T const* const end)
{
TR_ASSERT(iter == this->end()); // tr_buffer only supports appending
evbuffer_add(buf_.get(), begin, end - begin);
}
private:
evhelpers::evbuffer_unique_ptr buf_{ evbuffer_new() };
std::optional<evbuffer_iovec> reserved_space_;
[[nodiscard]] Iterator cbegin() const noexcept
{
return Iterator{ buf_.get(), 0U };
}
[[nodiscard]] Iterator cend() const noexcept
{
return Iterator{ buf_.get(), size() };
}
};
} // namespace libtransmission

View File

@ -277,40 +277,34 @@ namespace
{
namespace to_string_helpers
{
using OutBuf = libtransmission::Buffer;
using Buffer = libtransmission::Buffer;
void saveIntFunc(tr_variant const* val, void* vout)
{
auto out = static_cast<OutBuf*>(vout);
auto const [buf, buflen] = out->reserve_space(64U);
auto* walk = reinterpret_cast<char*>(buf);
auto const* const begin = walk;
walk = fmt::format_to(walk, FMT_COMPILE("i{:d}e"), val->val.i);
out->commit_space(walk - begin);
auto buf = std::array<char, 64>{};
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("i{:d}e"), val->val.i);
static_cast<Buffer*>(vout)->add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
}
void saveBoolFunc(tr_variant const* val, void* vout)
{
static_cast<OutBuf*>(vout)->add(val->val.b ? "i1e"sv : "i0e"sv);
static_cast<Buffer*>(vout)->add(val->val.b ? "i1e"sv : "i0e"sv);
}
void saveStringImpl(OutBuf* out, std::string_view sv)
void saveStringImpl(Buffer* tgt, std::string_view sv)
{
// `${sv.size()}:${sv}`
auto const [buf, buflen] = out->reserve_space(std::size(sv) + 32U);
auto* walk = reinterpret_cast<char*>(buf);
auto const* const begin = walk;
walk = fmt::format_to(walk, FMT_COMPILE("{:d}:"), std::size(sv));
walk = std::copy_n(std::data(sv), std::size(sv), walk);
out->commit_space(walk - begin);
auto prefix = std::array<char, 32>{};
auto const* const out = fmt::format_to(std::data(prefix), FMT_COMPILE("{:d}:"), std::size(sv));
tgt->add(std::data(prefix), out - std::data(prefix));
tgt->add(sv);
}
void saveStringFunc(tr_variant const* v, void* vout)
{
auto sv = std::string_view{};
(void)!tr_variantGetStrView(v, &sv);
saveStringImpl(static_cast<OutBuf*>(vout), sv);
saveStringImpl(static_cast<Buffer*>(vout), sv);
}
void saveRealFunc(tr_variant const* val, void* vout)
@ -319,22 +313,22 @@ void saveRealFunc(tr_variant const* val, void* vout)
auto buf = std::array<char, 64>{};
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:f}"), val->val.d);
saveStringImpl(static_cast<OutBuf*>(vout), { std::data(buf), static_cast<size_t>(out - std::data(buf)) });
saveStringImpl(static_cast<Buffer*>(vout), { std::data(buf), static_cast<size_t>(out - std::data(buf)) });
}
void saveDictBeginFunc(tr_variant const* /*val*/, void* vbuf)
{
static_cast<OutBuf*>(vbuf)->push_back('d');
static_cast<Buffer*>(vbuf)->push_back('d');
}
void saveListBeginFunc(tr_variant const* /*val*/, void* vbuf)
{
static_cast<OutBuf*>(vbuf)->push_back('l');
static_cast<Buffer*>(vbuf)->push_back('l');
}
void saveContainerEndFunc(tr_variant const* /*val*/, void* vbuf)
{
static_cast<OutBuf*>(vbuf)->push_back('e');
static_cast<Buffer*>(vbuf)->push_back('e');
}
struct VariantWalkFuncs const walk_funcs = {
@ -354,7 +348,7 @@ std::string tr_variantToStrBenc(tr_variant const* top)
{
using namespace to_string_helpers;
auto buf = OutBuf{};
auto buf = libtransmission::Buffer{};
tr_variantWalk(top, &walk_funcs, &buf, true);
return buf.to_string();
}

View File

@ -36,6 +36,7 @@
#include "libtransmission/variant.h"
using namespace std::literals;
using Buffer = libtransmission::Buffer;
namespace
{
@ -442,7 +443,7 @@ struct JsonWalk
}
std::deque<ParentState> parents;
libtransmission::Buffer out;
Buffer out;
bool doIndent;
};
@ -546,27 +547,25 @@ void jsonBoolFunc(tr_variant const* val, void* vdata)
void jsonRealFunc(tr_variant const* val, void* vdata)
{
auto* const data = static_cast<struct JsonWalk*>(vdata);
auto const [buf, buflen] = data->out.reserve_space(64);
auto* walk = reinterpret_cast<char*>(buf);
auto const* const begin = walk;
auto* data = static_cast<struct JsonWalk*>(vdata);
if (fabs(val->val.d - (int)val->val.d) < 0.00001)
{
walk = fmt::format_to(walk, FMT_COMPILE("{:.0f}"), val->val.d);
auto buf = std::array<char, 64>{};
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:.0f}"), val->val.d);
data->out.add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
}
else
{
walk = fmt::format_to(walk, FMT_COMPILE("{:.4f}"), val->val.d);
auto buf = std::array<char, 64>{};
auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:.4f}"), val->val.d);
data->out.add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
}
data->out.commit_space(walk - begin);
jsonChildFunc(data);
}
[[nodiscard]] char* write_escaped_char(char* buf, char const* const end, std::string_view& sv)
void write_escaped_char(Buffer& out, std::string_view& sv)
{
auto u16buf = std::array<std::uint16_t, 2>{};
@ -578,97 +577,85 @@ void jsonRealFunc(tr_variant const* val, void* vdata)
for (auto it = std::cbegin(u16buf); it != end16; ++it)
{
buf = fmt::format_to_n(buf, end - buf - 1, FMT_COMPILE("\\u{:04x}"), *it).out;
auto arr = std::array<char, 16>{};
auto const result = fmt::format_to_n(std::data(arr), std::size(arr), FMT_COMPILE("\\u{:04x}"), *it);
out.add(std::data(arr), result.size);
}
sv.remove_prefix(walk8 - begin8 - 1);
return buf;
}
void jsonStringFunc(tr_variant const* val, void* vdata)
{
auto* const data = static_cast<struct JsonWalk*>(vdata);
auto* data = static_cast<struct JsonWalk*>(vdata);
auto sv = std::string_view{};
(void)!tr_variantGetStrView(val, &sv);
auto& out = data->out;
auto const [buf, buflen] = out.reserve_space(std::size(sv) * 6 + 2);
auto* walk = reinterpret_cast<char*>(buf);
auto const* const begin = walk;
auto const* const end = begin + buflen;
*walk++ = '"';
out.reserve(std::size(data->out) + std::size(sv) * 6 + 2);
out.push_back('"');
for (; !std::empty(sv); sv.remove_prefix(1))
{
switch (sv.front())
{
case '\b':
*walk++ = '\\';
*walk++ = 'b';
out.add(R"(\b)"sv);
break;
case '\f':
*walk++ = '\\';
*walk++ = 'f';
out.add(R"(\f)"sv);
break;
case '\n':
*walk++ = '\\';
*walk++ = 'n';
out.add(R"(\n)"sv);
break;
case '\r':
*walk++ = '\\';
*walk++ = 'r';
out.add(R"(\r)"sv);
break;
case '\t':
*walk++ = '\\';
*walk++ = 't';
out.add(R"(\t)"sv);
break;
case '"':
*walk++ = '\\';
*walk++ = '"';
out.add(R"(\")"sv);
break;
case '\\':
*walk++ = '\\';
*walk++ = '\\';
out.add(R"(\\)"sv);
break;
default:
if (isprint((unsigned char)sv.front()) != 0)
{
*walk++ = sv.front();
out.push_back(sv.front());
}
else
{
try
{
walk = write_escaped_char(walk, end, sv);
write_escaped_char(out, sv);
}
catch (utf8::exception const&)
{
*walk++ = '?';
out.push_back('?');
}
}
break;
}
}
*walk++ = '"';
TR_ASSERT(walk <= end);
out.commit_space(walk - begin);
out.push_back('"');
jsonChildFunc(data);
}
void jsonDictBeginFunc(tr_variant const* val, void* vdata)
{
auto* const data = static_cast<struct JsonWalk*>(vdata);
auto* data = static_cast<struct JsonWalk*>(vdata);
jsonPushParent(data, val);
data->out.push_back('{');
@ -682,7 +669,7 @@ void jsonDictBeginFunc(tr_variant const* val, void* vdata)
void jsonListBeginFunc(tr_variant const* val, void* vdata)
{
size_t const n_children = tr_variantListSize(val);
auto* const data = static_cast<struct JsonWalk*>(vdata);
auto* data = static_cast<struct JsonWalk*>(vdata);
jsonPushParent(data, val);
data->out.push_back('[');
@ -695,7 +682,7 @@ void jsonListBeginFunc(tr_variant const* val, void* vdata)
void jsonContainerEndFunc(tr_variant const* val, void* vdata)
{
auto* const data = static_cast<struct JsonWalk*>(vdata);
auto* data = static_cast<struct JsonWalk*>(vdata);
jsonPopParent(data);

View File

@ -683,8 +683,7 @@ protected:
size_t idx = {};
};
template<typename ByKeyContainer>
void sort(ByKeyContainer& sortbuf)
void sort(std::vector<ByKey>& sortbuf)
{
if (!tr_variantIsDict(&v))
{

View File

@ -551,7 +551,7 @@ TEST_F(AnnouncerUdpTest, handleMessageReturnsFalseOnInvalidMessage)
EXPECT_FALSE(announcer->handle_message(std::data(arr), response_size));
// send a connection response but with an *invalid* action
buf = {};
buf.clear();
buf.add_uint32(ScrapeAction);
buf.add_uint32(transaction_id);
buf.add_uint64(tr_rand_obj<uint64_t>());

View File

@ -108,7 +108,6 @@ TEST_F(BufferTest, Move)
EXPECT_EQ(3U, std::size(a));
}
#if 0
TEST_F(BufferTest, NonBufferWriter)
{
auto constexpr Hello = "Hello, "sv;
@ -142,4 +141,3 @@ TEST_F(BufferTest, NonBufferWriter)
auto const result2 = std::string_view{ reinterpret_cast<char const*>(std::data(out2_vec)), std::size(out2_vec) };
EXPECT_EQ(result1, result2);
}
#endif