From 51fd7056bafa3bcb1bafc48a1cb113129bb31290 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 12 May 2023 11:15:15 -0500 Subject: [PATCH] refactor: add BufferReader, BufferWriter::reserve_space() (#5513) --- libtransmission/announcer-udp.cc | 9 +- libtransmission/peer-io.cc | 24 - libtransmission/peer-io.h | 17 +- libtransmission/peer-mse.h | 14 + libtransmission/peer-msgs.cc | 24 +- libtransmission/peer-socket.cc | 4 +- libtransmission/tr-buffer.h | 463 +++++++------------- libtransmission/variant-benc.cc | 38 +- libtransmission/variant-json.cc | 70 +-- tests/libtransmission/announcer-udp-test.cc | 2 +- tests/libtransmission/buffer-test.cc | 2 + 11 files changed, 259 insertions(+), 408 deletions(-) diff --git a/libtransmission/announcer-udp.cc b/libtransmission/announcer-udp.cc index 7620217fe..0f89d72ee 100644 --- a/libtransmission/announcer-udp.cc +++ b/libtransmission/announcer-udp.cc @@ -227,8 +227,7 @@ struct tau_announce_request response.leechers = buf.to_uint32(); response.seeders = buf.to_uint32(); - auto const [bytes, n_bytes] = buf.pullup(); - response.pex = tr_pex::from_compact_ipv4(bytes, n_bytes, nullptr, 0); + response.pex = tr_pex::from_compact_ipv4(std::data(buf), std::size(buf), nullptr, 0); requestFinished(); } else @@ -378,8 +377,7 @@ struct tau_tracker buf.add_uint32(TAU_ACTION_CONNECT); buf.add_uint32(this->connection_transaction_id); - auto const [bytes, n_bytes] = buf.pullup(); - this->sendto(bytes, n_bytes); + this->sendto(std::data(buf), std::size(buf)); } if (timeout_reqs) @@ -539,8 +537,7 @@ private: buf.add_uint64(this->connection_id); buf.add(payload, payload_len); - auto const [bytes, n_bytes] = buf.pullup(); - this->sendto(bytes, n_bytes); + this->sendto(std::data(buf), std::size(buf)); } public: diff --git a/libtransmission/peer-io.cc b/libtransmission/peer-io.cc index aa95744e9..8d0685a44 100644 --- a/libtransmission/peer-io.cc +++ b/libtransmission/peer-io.cc @@ -575,30 +575,6 @@ 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 07c36a75d..e76114b21 100644 --- a/libtransmission/peer-io.h +++ b/libtransmission/peer-io.h @@ -123,11 +123,24 @@ public: [[nodiscard]] size_t get_write_buffer_space(uint64_t now) const noexcept; - void write_bytes(void const* bytes, size_t n_bytes, bool is_piece_data); + 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); + } // Write all the data from `buf`. // This is a destructive add: `buf` is empty after this call. - void write(libtransmission::Buffer& buf, bool is_piece_data); + 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); + } size_t flush_outgoing_protocol_msgs(); diff --git a/libtransmission/peer-mse.h b/libtransmission/peer-mse.h index d8c4b0a6c..b1010446c 100644 --- a/libtransmission/peer-mse.h +++ b/libtransmission/peer-mse.h @@ -11,6 +11,7 @@ #error only libtransmission should #include this header. #endif +#include // for std::copy_n #include #include // size_t, std::byte #include @@ -99,6 +100,19 @@ 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 5e110626f..b8ebfa26b 100644 --- a/libtransmission/peer-msgs.cc +++ b/libtransmission/peer-msgs.cc @@ -791,15 +791,12 @@ void build_peer_message(tr_peerMsgsImpl const* const msgs, Buffer& out, uint8_t { 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 @@ -1015,7 +1012,7 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload) { msgs->peerSentLtepHandshake = true; - auto const handshake_sv = payload.pullup_sv(); + auto const handshake_sv = payload.to_string_view(); auto val = tr_variant{}; if (!tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, handshake_sv) || !tr_variantIsDict(&val)) @@ -1127,7 +1124,7 @@ void parseUtMetadata(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload_in) int64_t piece = -1; int64_t total_size = 0; - auto const tmp = payload_in.pullup_sv(); + auto const tmp = payload_in.to_string_view(); auto const* const msg_end = std::data(tmp) + std::size(tmp); auto dict = tr_variant{}; @@ -1184,7 +1181,7 @@ void parseUtPex(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload) return; } - auto const tmp = payload.pullup_sv(); + auto const tmp = payload.to_string_view(); if (tr_variant val; tr_variantFromBuf(&val, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, tmp)) { @@ -1448,15 +1445,12 @@ ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmissi break; case BtPeerMsgs::Bitfield: - { - 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; - } + 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; case BtPeerMsgs::Request: { diff --git a/libtransmission/peer-socket.cc b/libtransmission/peer-socket.cc index c56fa2738..34b29421b 100644 --- a/libtransmission/peer-socket.cc +++ b/libtransmission/peer-socket.cc @@ -85,7 +85,9 @@ size_t tr_peer_socket::try_write(Buffer& buf, size_t max, tr_error** error) cons #ifdef WITH_UTP if (is_utp()) { - auto const [data, datalen] = buf.pullup(); + // 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); errno = 0; auto const n_written = utp_write(handle.utp, data, std::min(datalen, max)); diff --git a/libtransmission/tr-buffer.h b/libtransmission/tr-buffer.h index e8c1d4577..a7ddec834 100644 --- a/libtransmission/tr-buffer.h +++ b/libtransmission/tr-buffer.h @@ -5,15 +5,18 @@ #pragma once -#include +#include // for std::byte #include #include #include +#include #include #include #include +#include + #include "error.h" #include "net.h" // tr_socket_t #include "tr-assert.h" @@ -23,21 +26,117 @@ namespace libtransmission { -template +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 class BufferWriter { public: - BufferWriter(T* out) - : out_{ out } - { - static_assert(sizeof(ValueType) == 1); - } + virtual ~BufferWriter() = default; + virtual std::pair reserve_space(size_t n_bytes) = 0; + virtual void commit_space(size_t n_bytes) = 0; void add(void const* span_begin, size_t span_len) { - auto const* const begin = reinterpret_cast(span_begin); - auto const* const end = begin + span_len; - out_->insert(std::end(*out_), begin, end); + auto [buf, buflen] = reserve_space(span_len); + std::copy_n(reinterpret_cast(span_begin), span_len, buf); + commit_space(span_len); } template @@ -96,344 +195,78 @@ public: add(&nport, sizeof(nport)); } -private: - T* out_; + 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); + fmt::print("{:p} read {:d} bytes (of {:d}) from socket {:d}\n", fmt::ptr(this), n_read, n_bytes, sockfd); + return n_read; + } + + auto const err = sockerrno; + tr_error_set(error, err, tr_net_strerror(err)); + fmt::print("{:p} error {:s}\n", fmt::ptr(this), tr_net_strerror(err)); + return {}; + } }; -class Buffer : public BufferWriter +class Buffer final + : public BufferReader + , public BufferWriter { public: - 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; - } + using value_type = std::byte; + 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]] auto size() const noexcept + [[nodiscard]] size_t size() const noexcept override { return evbuffer_get_length(buf_.get()); } - [[nodiscard]] auto empty() const noexcept - { - return evbuffer_get_length(buf_.get()) == 0; - } - - [[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) + void drain(size_t n_bytes) override { evbuffer_drain(buf_.get(), n_bytes); } - void clear() + [[nodiscard]] value_type const* data() const override { - drain(size()); + return reinterpret_cast(evbuffer_pullup(buf_.get(), -1)); } - // 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) + virtual std::pair reserve_space(size_t n_bytes) override { - 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; + 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) }; } - [[nodiscard]] std::pair pullup() + virtual void commit_space(size_t n_bytes) override { - 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); + 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(); } private: evhelpers::evbuffer_unique_ptr buf_{ evbuffer_new() }; - - [[nodiscard]] Iterator cbegin() const noexcept - { - return Iterator{ buf_.get(), 0U }; - } - - [[nodiscard]] Iterator cend() const noexcept - { - return Iterator{ buf_.get(), size() }; - } + std::optional reserved_space_; }; } // namespace libtransmission diff --git a/libtransmission/variant-benc.cc b/libtransmission/variant-benc.cc index 4ff4bf088..383f53deb 100644 --- a/libtransmission/variant-benc.cc +++ b/libtransmission/variant-benc.cc @@ -277,34 +277,40 @@ namespace { namespace to_string_helpers { -using Buffer = libtransmission::Buffer; +using OutBuf = libtransmission::Buffer; void saveIntFunc(tr_variant const* val, void* vout) { - 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))); + 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); } 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(Buffer* tgt, std::string_view sv) +void saveStringImpl(OutBuf* out, std::string_view sv) { // `${sv.size()}:${sv}` - 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); + 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); } 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) @@ -313,22 +319,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 = { @@ -348,7 +354,7 @@ std::string tr_variantToStrBenc(tr_variant const* top) { using namespace to_string_helpers; - auto buf = libtransmission::Buffer{}; + auto buf = OutBuf{}; tr_variantWalk(top, &walk_funcs, &buf, true); return buf.to_string(); } diff --git a/libtransmission/variant-json.cc b/libtransmission/variant-json.cc index 42073be8d..6ab2e2e68 100644 --- a/libtransmission/variant-json.cc +++ b/libtransmission/variant-json.cc @@ -547,25 +547,27 @@ void jsonBoolFunc(tr_variant const* val, void* vdata) void jsonRealFunc(tr_variant const* val, void* vdata) { - auto* data = static_cast(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; if (fabs(val->val.d - (int)val->val.d) < 0.00001) { - 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))); + walk = fmt::format_to(walk, FMT_COMPILE("{:.0f}"), val->val.d); } else { - 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))); + walk = fmt::format_to(walk, FMT_COMPILE("{:.4f}"), val->val.d); } + data->out.commit_space(walk - begin); + jsonChildFunc(data); } -void write_escaped_char(Buffer& out, std::string_view& sv) +[[nodiscard]] char* write_escaped_char(char* buf, char const* const end, std::string_view& sv) { auto u16buf = std::array{}; @@ -577,85 +579,97 @@ void write_escaped_char(Buffer& out, std::string_view& sv) for (auto it = std::cbegin(u16buf); it != end16; ++it) { - 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); + buf = fmt::format_to_n(buf, end - buf - 1, FMT_COMPILE("\\u{:04x}"), *it).out; } sv.remove_prefix(walk8 - begin8 - 1); + return buf; } void jsonStringFunc(tr_variant const* val, void* vdata) { - auto* data = static_cast(vdata); + auto* const data = static_cast(vdata); auto sv = std::string_view{}; (void)!tr_variantGetStrView(val, &sv); auto& out = data->out; - out.reserve(std::size(data->out) + std::size(sv) * 6 + 2); - out.push_back('"'); + 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++ = '"'; for (; !std::empty(sv); sv.remove_prefix(1)) { switch (sv.front()) { case '\b': - out.add(R"(\b)"sv); + *walk++ = '\\'; + *walk++ = 'b'; break; case '\f': - out.add(R"(\f)"sv); + *walk++ = '\\'; + *walk++ = 'f'; break; case '\n': - out.add(R"(\n)"sv); + *walk++ = '\\'; + *walk++ = 'n'; break; case '\r': - out.add(R"(\r)"sv); + *walk++ = '\\'; + *walk++ = 'r'; break; case '\t': - out.add(R"(\t)"sv); + *walk++ = '\\'; + *walk++ = 't'; break; case '"': - out.add(R"(\")"sv); + *walk++ = '\\'; + *walk++ = '"'; break; case '\\': - out.add(R"(\\)"sv); + *walk++ = '\\'; + *walk++ = '\\'; break; default: if (isprint((unsigned char)sv.front()) != 0) { - out.push_back(sv.front()); + *walk++ = sv.front(); } else { try { - write_escaped_char(out, sv); + walk = write_escaped_char(walk, end, sv); } catch (utf8::exception const&) { - out.push_back('?'); + *walk++ = '?'; } } break; } } - out.push_back('"'); + *walk++ = '"'; + TR_ASSERT(walk <= end); + out.commit_space(walk - begin); jsonChildFunc(data); } void jsonDictBeginFunc(tr_variant const* val, void* vdata) { - auto* data = static_cast(vdata); + auto* const data = static_cast(vdata); jsonPushParent(data, val); data->out.push_back('{'); @@ -669,7 +683,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* data = static_cast(vdata); + auto* const data = static_cast(vdata); jsonPushParent(data, val); data->out.push_back('['); @@ -682,7 +696,7 @@ void jsonListBeginFunc(tr_variant const* val, void* vdata) void jsonContainerEndFunc(tr_variant const* val, void* vdata) { - auto* data = static_cast(vdata); + auto* const data = static_cast(vdata); jsonPopParent(data); diff --git a/tests/libtransmission/announcer-udp-test.cc b/tests/libtransmission/announcer-udp-test.cc index 6b50f33f9..f92cbe45d 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.clear(); + buf = {}; 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 3e3576bc3..dd99cdf2e 100644 --- a/tests/libtransmission/buffer-test.cc +++ b/tests/libtransmission/buffer-test.cc @@ -108,6 +108,7 @@ TEST_F(BufferTest, Move) EXPECT_EQ(3U, std::size(a)); } +#if 0 TEST_F(BufferTest, NonBufferWriter) { auto constexpr Hello = "Hello, "sv; @@ -141,3 +142,4 @@ 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