diff --git a/libtransmission/announcer-udp.cc b/libtransmission/announcer-udp.cc index 2aade4a62..7620217fe 100644 --- a/libtransmission/announcer-udp.cc +++ b/libtransmission/announcer-udp.cc @@ -49,8 +49,6 @@ using namespace std::literals; using tau_connection_t = uint64_t; using tau_transaction_t = uint32_t; -using InBuf = libtransmission::BufferReader; - 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(&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: diff --git a/libtransmission/peer-io.cc b/libtransmission/peer-io.cc index 8d0685a44..aa95744e9 100644 --- a/libtransmission/peer-io.cc +++ b/libtransmission/peer-io.cc @@ -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) diff --git a/libtransmission/peer-io.h b/libtransmission/peer-io.h index e76114b21..07c36a75d 100644 --- a/libtransmission/peer-io.h +++ b/libtransmission/peer-io.h @@ -123,24 +123,11 @@ public: [[nodiscard]] size_t get_write_buffer_space(uint64_t now) const noexcept; - template - 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(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& 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(); diff --git a/libtransmission/peer-mse.h b/libtransmission/peer-mse.h index b1010446c..d8c4b0a6c 100644 --- a/libtransmission/peer-mse.h +++ b/libtransmission/peer-mse.h @@ -11,7 +11,6 @@ #error only libtransmission should #include this header. #endif -#include // for std::copy_n #include #include // size_t, std::byte #include @@ -100,19 +99,6 @@ public: } } - template - 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_; diff --git a/libtransmission/peer-msgs.cc b/libtransmission/peer-msgs.cc index a49388700..b1d7c35d1 100644 --- a/libtransmission/peer-msgs.cc +++ b/libtransmission/peer-msgs.cc @@ -47,8 +47,7 @@ #endif using namespace std::literals; -using PeerMessageBuffer = libtransmission::Buffer; -using PeerMessageReader = libtransmission::BufferReader; +using Buffer = libtransmission::Buffer; namespace { @@ -219,7 +218,7 @@ struct tr_incoming { std::optional length; // the full message payload length. Includes the +1 for id length std::optional 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 // --- -template -void add_param(BufferWriter& buffer, uint8_t param) noexcept +void add_param(Buffer& buffer, uint8_t param) noexcept { buffer.add_uint8(param); } -template -void add_param(BufferWriter& buffer, uint16_t param) noexcept +void add_param(Buffer& buffer, uint16_t param) noexcept { buffer.add_uint16(param); } -template -void add_param(BufferWriter& buffer, uint32_t param) noexcept +void add_param(Buffer& buffer, uint32_t param) noexcept { buffer.add_uint32(param); } -template -void add_param(BufferWriter& buffer, T const& param) noexcept +template +void add_param(Buffer& buffer, T const& param) noexcept { buffer.add(param); } @@ -814,17 +810,20 @@ template } } // namespace -template -void build_peer_message(tr_peerMsgsImpl const* const msgs, BufferWriter& out, uint8_t type, Args const&... args) +template +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 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) { // 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(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(buf), buflen); + msgs->publish(tr_peer_event::GotBitfield(&msgs->have_)); + msgs->invalidatePercentDone(); + break; + } case BtPeerMsgs::Request: { diff --git a/libtransmission/peer-socket.cc b/libtransmission/peer-socket.cc index 984e8148e..c56fa2738 100644 --- a/libtransmission/peer-socket.cc +++ b/libtransmission/peer-socket.cc @@ -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::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)); diff --git a/libtransmission/peer-socket.h b/libtransmission/peer-socket.h index f0ef4684a..f57ab7ad4 100644 --- a/libtransmission/peer-socket.h +++ b/libtransmission/peer-socket.h @@ -28,7 +28,6 @@ class tr_peer_socket { public: using Buffer = libtransmission::Buffer; - using OutBuf = libtransmission::BufferReader; 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 socketAddress() const noexcept { diff --git a/libtransmission/tr-buffer.h b/libtransmission/tr-buffer.h index 4a8702758..e8c1d4577 100644 --- a/libtransmission/tr-buffer.h +++ b/libtransmission/tr-buffer.h @@ -5,18 +5,15 @@ #pragma once -#include // for std::byte +#include #include #include #include -#include #include #include #include -#include - #include "error.h" #include "net.h" // tr_socket_t #include "tr-assert.h" @@ -26,117 +23,21 @@ namespace libtransmission { -template -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(data()), size() }; - } - - [[nodiscard]] auto to_string_view() const - { - return std::string_view{ reinterpret_cast(data()), size() }; - } - - template - [[nodiscard]] bool starts_with(T const& needle) const - { - auto const n_bytes = std::size(needle); - auto const needle_begin = reinterpret_cast(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(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(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 +template class BufferWriter { public: - virtual ~BufferWriter() = default; - virtual std::pair 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(span_begin), span_len, buf); - commit_space(span_len); + auto const* const begin = reinterpret_cast(span_begin); + auto const* const end = begin + span_len; + out_->insert(std::end(*out_), begin, end); } template @@ -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(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 - , public BufferWriter +class Buffer : public BufferWriter { 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(info.iov.iov_base)[info.offset]; + } + + [[nodiscard]] value_type operator*() const noexcept + { + auto const& info = iov(); + return static_cast(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::max(), &ptr, &iov.iov, 1); + iov.offset = 0; + iov_ = iov; + } + + return *iov_; + } + + mutable std::optional iov_; + + evbuffer* buf_; + size_t buf_offset_ = 0; + }; + + Buffer() + : BufferWriter{ this } + { + } + + Buffer(Buffer&& that) + : BufferWriter(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 explicit Buffer(T const& data) + : BufferWriter{ 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(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 + [[nodiscard]] TR_CONSTEXPR20 bool starts_with(T const& needle) const + { + auto const n_bytes = std::size(needle); + auto const needle_begin = reinterpret_cast(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 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(iov.iov_base), static_cast(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(res); + } + tr_error_set(error, err, tr_net_strerror(err)); + return 0; + } + + [[nodiscard]] std::pair pullup() + { + return { reinterpret_cast(evbuffer_pullup(buf_.get(), -1)), size() }; + } + + [[nodiscard]] std::byte const* data() const + { + return reinterpret_cast(evbuffer_pullup(buf_.get(), -1)); + } + + [[nodiscard]] auto pullup_sv() + { + auto const [buf, buflen] = pullup(); + return std::string_view{ reinterpret_cast(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(n_bytes)); + auto const err = EVUTIL_SOCKET_ERROR(); + + if (res > 0) + { + return static_cast(res); + } + + if (res == 0) + { + tr_error_set_from_errno(error, ENOTCONN); + } + else + { + tr_error_set(error, err, tr_net_strerror(err)); + } + + return {}; + } + + template + 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 reserved_space_; + + [[nodiscard]] Iterator cbegin() const noexcept + { + return Iterator{ buf_.get(), 0U }; + } + + [[nodiscard]] Iterator cend() const noexcept + { + return Iterator{ buf_.get(), size() }; + } }; } // namespace libtransmission diff --git a/libtransmission/variant-benc.cc b/libtransmission/variant-benc.cc index 383f53deb..4ff4bf088 100644 --- a/libtransmission/variant-benc.cc +++ b/libtransmission/variant-benc.cc @@ -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(vout); - - auto const [buf, buflen] = out->reserve_space(64U); - auto* walk = reinterpret_cast(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{}; + auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("i{:d}e"), val->val.i); + static_cast(vout)->add(std::data(buf), static_cast(out - std::data(buf))); } void saveBoolFunc(tr_variant const* val, void* vout) { - static_cast(vout)->add(val->val.b ? "i1e"sv : "i0e"sv); + static_cast(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(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{}; + 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(vout), sv); + saveStringImpl(static_cast(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{}; auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:f}"), val->val.d); - saveStringImpl(static_cast(vout), { std::data(buf), static_cast(out - std::data(buf)) }); + saveStringImpl(static_cast(vout), { std::data(buf), static_cast(out - std::data(buf)) }); } void saveDictBeginFunc(tr_variant const* /*val*/, void* vbuf) { - static_cast(vbuf)->push_back('d'); + static_cast(vbuf)->push_back('d'); } void saveListBeginFunc(tr_variant const* /*val*/, void* vbuf) { - static_cast(vbuf)->push_back('l'); + static_cast(vbuf)->push_back('l'); } void saveContainerEndFunc(tr_variant const* /*val*/, void* vbuf) { - static_cast(vbuf)->push_back('e'); + static_cast(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(); } diff --git a/libtransmission/variant-json.cc b/libtransmission/variant-json.cc index 91f94fad7..42073be8d 100644 --- a/libtransmission/variant-json.cc +++ b/libtransmission/variant-json.cc @@ -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 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(vdata); - - auto const [buf, buflen] = data->out.reserve_space(64); - auto* walk = reinterpret_cast(buf); - auto const* const begin = walk; + auto* data = static_cast(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{}; + auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:.0f}"), val->val.d); + data->out.add(std::data(buf), static_cast(out - std::data(buf))); } else { - walk = fmt::format_to(walk, FMT_COMPILE("{:.4f}"), val->val.d); + auto buf = std::array{}; + auto const* const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:.4f}"), val->val.d); + data->out.add(std::data(buf), static_cast(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{}; @@ -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{}; + 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(vdata); + auto* data = static_cast(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(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(vdata); + auto* data = static_cast(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(vdata); + auto* data = static_cast(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(vdata); + auto* data = static_cast(vdata); jsonPopParent(data); diff --git a/libtransmission/variant.cc b/libtransmission/variant.cc index 71cb303f6..d62544e93 100644 --- a/libtransmission/variant.cc +++ b/libtransmission/variant.cc @@ -683,8 +683,7 @@ protected: size_t idx = {}; }; - template - void sort(ByKeyContainer& sortbuf) + void sort(std::vector& sortbuf) { if (!tr_variantIsDict(&v)) { diff --git a/tests/libtransmission/announcer-udp-test.cc b/tests/libtransmission/announcer-udp-test.cc index f92cbe45d..6b50f33f9 100644 --- a/tests/libtransmission/announcer-udp-test.cc +++ b/tests/libtransmission/announcer-udp-test.cc @@ -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()); diff --git a/tests/libtransmission/buffer-test.cc b/tests/libtransmission/buffer-test.cc index dd99cdf2e..3e3576bc3 100644 --- a/tests/libtransmission/buffer-test.cc +++ b/tests/libtransmission/buffer-test.cc @@ -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(std::data(out2_vec)), std::size(out2_vec) }; EXPECT_EQ(result1, result2); } -#endif