diff --git a/libtransmission/announcer-udp.cc b/libtransmission/announcer-udp.cc index 7620217fe..2aade4a62 100644 --- a/libtransmission/announcer-udp.cc +++ b/libtransmission/announcer-udp.cc @@ -49,6 +49,8 @@ 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() @@ -114,7 +116,7 @@ struct tau_scrape_request requestFinished(); } - void onResponse(tau_action_t action, libtransmission::Buffer& buf) + void onResponse(tau_action_t action, InBuf& buf) { response.did_connect = true; response.did_timeout = false; @@ -214,7 +216,7 @@ struct tau_announce_request this->requestFinished(); } - void onResponse(tau_action_t action, libtransmission::Buffer& buf) + void onResponse(tau_action_t action, InBuf& buf) { auto const buflen = std::size(buf); @@ -227,8 +229,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 @@ -310,7 +311,7 @@ struct tau_tracker mediator_.sendto(buf, buflen, reinterpret_cast(&ss), sslen); } - void on_connection_response(tau_action_t action, libtransmission::Buffer& buf) + void on_connection_response(tau_action_t action, InBuf& buf) { this->connecting_at = 0; this->connection_transaction_id = 0; @@ -378,8 +379,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 +539,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.h b/libtransmission/peer-io.h index 683826316..5580e860b 100644 --- a/libtransmission/peer-io.h +++ b/libtransmission/peer-io.h @@ -139,7 +139,8 @@ public: // 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) + template + 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); diff --git a/libtransmission/peer-msgs.cc b/libtransmission/peer-msgs.cc index 781ac8d8a..41632d94f 100644 --- a/libtransmission/peer-msgs.cc +++ b/libtransmission/peer-msgs.cc @@ -47,7 +47,9 @@ #endif using namespace std::literals; -using Buffer = libtransmission::Buffer; +using MessageBuffer = libtransmission::Buffer; +using MessageReader = libtransmission::BufferReader; +using MessageWriter = libtransmission::BufferWriter; namespace { @@ -218,7 +220,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 - Buffer payload; + MessageBuffer payload; struct incoming_piece_data { @@ -677,8 +679,8 @@ public: tr_bitfield have_; private: - friend ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmission::Buffer& payload); - friend void parseLtepHandshake(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload); + friend ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, MessageReader& payload); + friend void parseLtepHandshake(tr_peerMsgsImpl* msgs, MessageReader& payload); tr_peer_callback const callback_; void* const callback_data_; @@ -757,23 +759,23 @@ template // --- -void add_param(Buffer& buffer, uint8_t param) noexcept +void add_param(MessageWriter& buffer, uint8_t param) noexcept { buffer.add_uint8(param); } -void add_param(Buffer& buffer, uint16_t param) noexcept +void add_param(MessageWriter& buffer, uint16_t param) noexcept { buffer.add_uint16(param); } -void add_param(Buffer& buffer, uint32_t param) noexcept +void add_param(MessageWriter& buffer, uint32_t param) noexcept { buffer.add_uint32(param); } template -void add_param(Buffer& buffer, T const& param) noexcept +void add_param(MessageWriter& buffer, T const& param) noexcept { buffer.add(param); } @@ -811,19 +813,16 @@ template } // namespace template -void build_peer_message(tr_peerMsgsImpl const* const msgs, Buffer& out, uint8_t type, Args const&... args) +void build_peer_message(tr_peerMsgsImpl const* const msgs, MessageWriter& 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 @@ -833,7 +832,7 @@ size_t protocol_send_message(tr_peerMsgsImpl const* const msgs, uint8_t type, Ar { using namespace protocol_send_message_helpers; - auto out = Buffer{}; + auto out = MessageBuffer{}; build_peer_message(msgs, out, type, args...); auto const n_bytes_added = std::size(out); msgs->io->write(out, type == BtPeerMsgs::Piece); @@ -844,7 +843,7 @@ size_t protocol_send_keepalive(tr_peerMsgsImpl* msgs) { logtrace(msgs, "sending 'keepalive'"); - auto out = Buffer{}; + auto out = MessageBuffer{}; out.add_uint32(0); auto const n_bytes_added = std::size(out); @@ -1035,7 +1034,7 @@ void sendLtepHandshake(tr_peerMsgsImpl* msgs) tr_variantClear(&val); } -void parseLtepHandshake(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload) +void parseLtepHandshake(tr_peerMsgsImpl* msgs, MessageReader& payload) { msgs->peerSentLtepHandshake = true; @@ -1145,7 +1144,7 @@ void parseLtepHandshake(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload) tr_variantClear(&val); } -void parseUtMetadata(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload_in) +void parseUtMetadata(tr_peerMsgsImpl* msgs, MessageReader& payload_in) { int64_t msg_type = -1; int64_t piece = -1; @@ -1200,7 +1199,7 @@ void parseUtMetadata(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload_in) } } -void parseUtPex(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload) +void parseUtPex(tr_peerMsgsImpl* msgs, MessageReader& payload) { auto* const tor = msgs->torrent; if (!tor->allows_pex()) @@ -1248,7 +1247,7 @@ void parseUtPex(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload) } } -void parseLtep(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload) +void parseLtep(tr_peerMsgsImpl* msgs, MessageReader& payload) { TR_ASSERT(!std::empty(payload)); @@ -1283,7 +1282,7 @@ void parseLtep(tr_peerMsgsImpl* msgs, libtransmission::Buffer& payload) } } -ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmission::Buffer& payload); +ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, MessageReader& payload); void prefetchPieces(tr_peerMsgsImpl* msgs) { @@ -1348,7 +1347,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, libtransmission::Buffer& payload) +ReadResult read_piece_data(tr_peerMsgsImpl* msgs, MessageReader& payload) { // auto const piece = payload.to_uint32(); @@ -1396,7 +1395,7 @@ ReadResult read_piece_data(tr_peerMsgsImpl* msgs, libtransmission::Buffer& paylo return { ok ? READ_NOW : READ_ERR, len }; } -ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, libtransmission::Buffer& payload) +ReadResult process_peer_message(tr_peerMsgsImpl* msgs, uint8_t id, MessageReader& payload) { bool const fext = msgs->io->supports_fext(); @@ -1477,15 +1476,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: { @@ -1785,7 +1781,7 @@ ReadState canRead(tr_peerIo* io, void* vmsgs, size_t* piece) current_message_len.reset(); auto const message_type = *current_message_type; current_message_type.reset(); - auto payload = libtransmission::Buffer{}; + auto payload = MessageBuffer{}; std::swap(payload, current_payload); auto const [read_state, n_piece_bytes_read] = process_peer_message(msgs, message_type, payload); diff --git a/libtransmission/peer-socket.cc b/libtransmission/peer-socket.cc index c56fa2738..60f59b451 100644 --- a/libtransmission/peer-socket.cc +++ b/libtransmission/peer-socket.cc @@ -85,10 +85,10 @@ 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(); - errno = 0; - auto const n_written = utp_write(handle.utp, data, std::min(datalen, max)); + // NB: utp_write() does not modify its 2nd arg, but a wart in + // libutp's public API requires it to be non-const anyway :shrug: + auto const n_written = utp_write(handle.utp, const_cast(std::data(buf)), std::min(std::size(buf), max)); auto const error_code = errno; if (n_written > 0) diff --git a/libtransmission/tr-buffer.h b/libtransmission/tr-buffer.h index 24e84c489..4418ac111 100644 --- a/libtransmission/tr-buffer.h +++ b/libtransmission/tr-buffer.h @@ -39,6 +39,25 @@ public: return size() == 0; } + [[nodiscard]] auto const* begin() const noexcept + { + return data(); + } + + [[nodiscard]] auto const* end() const noexcept + { + return begin() + 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, data()); + } + [[nodiscard]] auto to_string_view() const { return std::string_view{ reinterpret_cast(data()), size() }; @@ -83,6 +102,27 @@ public: to_buf(&tmp, sizeof(tmp)); return tr_ntohll(tmp); } + + // 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) + { + n_bytes = std::min(n_bytes, size()); + + if (n_bytes == 0U) + { + return {}; + } + + if (auto const n_sent = send(sockfd, reinterpret_cast(data()), n_bytes, 0); n_sent >= 0U) + { + drain(n_sent); + return n_sent; + } + + auto const err = sockerrno; + tr_error_set(error, err, tr_net_strerror(err)); + return {}; + } }; template @@ -164,143 +204,6 @@ class Buffer final 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() = default; Buffer(Buffer&&) = default; Buffer(Buffer const&) = delete; @@ -320,7 +223,7 @@ public: return evbuffer_get_length(buf_.get()); } - [[nodiscard]] std::byte const* data() const override + [[nodiscard]] value_type const* data() const override { return reinterpret_cast(evbuffer_pullup(buf_.get(), -1)); } @@ -352,64 +255,6 @@ public: // - [[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()); - } - - void clear() - { - drain(size()); - } - - // 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) - { - 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() }; - } - - 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); @@ -436,16 +281,6 @@ public: 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 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..91f94fad7 100644 --- a/libtransmission/variant-json.cc +++ b/libtransmission/variant-json.cc @@ -36,7 +36,6 @@ #include "libtransmission/variant.h" using namespace std::literals; -using Buffer = libtransmission::Buffer; namespace { @@ -443,7 +442,7 @@ struct JsonWalk } std::deque parents; - Buffer out; + libtransmission::Buffer out; bool doIndent; }; @@ -547,25 +546,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 +578,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 +682,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 +695,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());