refactor: extract tr_buffer class from tr_peerIo (#3986)

This commit is contained in:
Charles Kerr
2022-10-19 11:42:08 -05:00
committed by GitHub
parent f22ce40c72
commit 450f1dcadc
15 changed files with 731 additions and 431 deletions

View File

@@ -184,6 +184,7 @@
A267927C130DFF2700CB7464 /* libutp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E38544130DFEE3001F501B /* libutp.a */; };
A2679294130E00A000CB7464 /* tr-utp.cc in Sources */ = {isa = PBXBuildFile; fileRef = A2679292130E00A000CB7464 /* tr-utp.cc */; };
A2679295130E00A000CB7464 /* tr-utp.h in Headers */ = {isa = PBXBuildFile; fileRef = A2679293130E00A000CB7464 /* tr-utp.h */; };
A263C6B1F6718E2486DB20E0 /* tr-buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = A263C6B1F6718E2486DB20E1 /* tr-buffer.h */; };
A26AF21A0D2DA35A00FF7140 /* FileOutlineController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A26AF2190D2DA35A00FF7140 /* FileOutlineController.mm */; };
A26AF27E0D2DBDDF00FF7140 /* AddWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = A26AF27C0D2DBDDF00FF7140 /* AddWindow.xib */; };
A26AF2840D2DC27C00FF7140 /* AddWindowController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A26AF2830D2DC27C00FF7140 /* AddWindowController.mm */; };
@@ -909,6 +910,7 @@
A2661D3B12D0E51B004F69D5 /* FilterBarView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FilterBarView.mm; sourceTree = "<group>"; };
A2679292130E00A000CB7464 /* tr-utp.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "tr-utp.cc"; sourceTree = "<group>"; };
A2679293130E00A000CB7464 /* tr-utp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "tr-utp.h"; sourceTree = "<group>"; };
A263C6B1F6718E2486DB20E1 /* tr-buffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "tr-buffer.h"; sourceTree = "<group>"; };
A26AF1050D2855FC00FF7140 /* ru */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
A26AF1070D2855FC00FF7140 /* ru */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
A26AF2180D2DA35A00FF7140 /* FileOutlineController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileOutlineController.h; sourceTree = "<group>"; };
@@ -1717,6 +1719,7 @@
A284214212DA663E00FBDDBB /* tr-udp.cc */,
A2679292130E00A000CB7464 /* tr-utp.cc */,
A2679293130E00A000CB7464 /* tr-utp.h */,
A263C6B1F6718E2486DB20E1 /* tr-buffer.h */,
BEFC1DF50C07861A00B0BB3C /* transmission.h */,
A24621360C769CF400088E81 /* trevent.cc */,
A24621350C769CF400088E81 /* trevent.h */,
@@ -2204,6 +2207,7 @@
11524394C75E57E52CD9ADF2 /* dns-ev.h in Headers */,
C1077A4F183EB29600634C22 /* error.h in Headers */,
A2679295130E00A000CB7464 /* tr-utp.h in Headers */,
A2679295130E00A000CB7464 /* tr-buffer.h in Headers */,
A23F29A1132A447400E9A83B /* announcer-common.h in Headers */,
A2EE726F14DCCC950093C99A /* port-forwarding-natpmp.h in Headers */,
A2D77451154CC25700A62B93 /* WebSeedTableView.h in Headers */,

View File

@@ -149,6 +149,7 @@ set(${PROJECT_NAME}_PUBLIC_HEADERS
tr-getopt.h
tr-macros.h
tr-strbuf.h
tr-buffer.h
transmission.h
utils.h
variant.h

View File

@@ -10,8 +10,6 @@
#include <string_view>
#include <utility>
#include <event2/buffer.h>
#include <fmt/format.h>
#include "transmission.h"
@@ -23,6 +21,7 @@
#include "peer-io.h"
#include "timer.h"
#include "tr-assert.h"
#include "tr-buffer.h"
#include "utils.h"
using namespace std::literals;
@@ -376,13 +375,12 @@ static constexpr uint32_t getCryptoSelect(tr_encryption_mode encryption_mode, ui
static ReadState readYb(tr_handshake* handshake, tr_peerIo* peer_io)
{
auto const* const peek = peer_io->peek(std::size(HandshakeName));
if (peek == nullptr)
if (peer_io->readBufferSize() < std::size(HandshakeName))
{
return READ_LATER;
}
bool const is_encrypted = !std::equal(std::begin(HandshakeName), std::end(HandshakeName), peek);
bool const is_encrypted = !peer_io->readBufferStartsWith(HandshakeName);
auto peer_public_key = DH::key_bigend_t{};
if (is_encrypted && (peer_io->readBufferSize() < std::size(peer_public_key)))
{
@@ -405,11 +403,10 @@ static ReadState readYb(tr_handshake* handshake, tr_peerIo* peer_io)
/* now send these: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S),
* ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)), ENCRYPT(IA) */
evbuffer* const outbuf = evbuffer_new();
auto outbuf = libtransmission::Buffer{};
/* HASH('req1', S) */
auto const req1 = tr_sha1::digest("req1"sv, handshake->dh.secret());
evbuffer_add(outbuf, std::data(req1), std::size(req1));
outbuf.add(tr_sha1::digest("req1"sv, handshake->dh.secret()));
auto const info_hash = peer_io->torrentHash();
if (!info_hash)
@@ -422,29 +419,29 @@ static ReadState readYb(tr_handshake* handshake, tr_peerIo* peer_io)
{
auto const req2 = tr_sha1::digest("req2"sv, *info_hash);
auto const req3 = tr_sha1::digest("req3"sv, handshake->dh.secret());
auto buf = tr_sha1_digest_t{};
for (size_t i = 0, n = std::size(buf); i < n; ++i)
auto x_or = tr_sha1_digest_t{};
for (size_t i = 0, n = std::size(x_or); i < n; ++i)
{
buf[i] = req2[i] ^ req3[i];
x_or[i] = req2[i] ^ req3[i];
}
evbuffer_add(outbuf, std::data(buf), std::size(buf));
outbuf.add(x_or);
}
/* ENCRYPT(VC, crypto_provide, len(PadC), PadC
* PadC is reserved for future extensions to the handshake...
* standard practice at this time is for it to be zero-length */
peer_io->writeBuf(outbuf, false);
peer_io->write(outbuf, false);
peer_io->encryptInit(peer_io->isIncoming(), handshake->dh, *info_hash);
evbuffer_add(outbuf, std::data(VC), std::size(VC));
evbuffer_add_uint32(outbuf, handshake->cryptoProvide());
evbuffer_add_uint16(outbuf, 0);
outbuf.add(VC);
outbuf.addUint32(handshake->cryptoProvide());
outbuf.addUint16(0);
/* ENCRYPT len(IA)), ENCRYPT(IA) */
if (auto msg = std::array<uint8_t, HandshakeSize>{}; buildHandshakeMessage(handshake, std::data(msg)))
{
evbuffer_add_uint16(outbuf, std::size(msg));
evbuffer_add(outbuf, std::data(msg), std::size(msg));
outbuf.addUint16(std::size(msg));
outbuf.add(msg);
handshake->haveSentBitTorrentHandshake = true;
}
else
@@ -454,10 +451,7 @@ static ReadState readYb(tr_handshake* handshake, tr_peerIo* peer_io)
/* send it */
setReadState(handshake, AWAITING_VC);
peer_io->writeBuf(outbuf, false);
/* cleanup */
evbuffer_free(outbuf);
peer_io->write(outbuf, false);
return READ_NOW;
}
@@ -473,14 +467,13 @@ static ReadState readVC(tr_handshake* handshake, tr_peerIo* peer_io)
for (size_t i = 0; i < PadbMaxlen; ++i)
{
auto const* const peek = peer_io->peek(std::size(needle));
if (peek == nullptr)
if (peer_io->readBufferSize() < std::size(needle))
{
tr_logAddTraceHand(handshake, "not enough bytes... returning read_more");
return READ_LATER;
}
if (std::equal(std::begin(needle), std::end(needle), peek))
if (peer_io->readBufferStartsWith(needle))
{
tr_logAddTraceHand(handshake, "got it!");
// We already know it's a match; now we just need to
@@ -561,17 +554,14 @@ static ReadState readHandshake(tr_handshake* handshake, tr_peerIo* peer_io)
{
tr_logAddTraceHand(handshake, fmt::format("payload: need {}, got {}", IncomingHandshakeLen, peer_io->readBufferSize()));
auto const* const peek = peer_io->peek(IncomingHandshakeLen);
if (peek == nullptr)
if (peer_io->readBufferSize() < IncomingHandshakeLen)
{
return READ_LATER;
}
handshake->haveReadAnythingFromPeer = true;
// peek instead of reading, because if we decide the handshake is
// encrypted we'll pass the unconsumed buffer to AWAITING_YA
if (std::equal(std::begin(HandshakeName), std::end(HandshakeName), peek)) // unencrypted
if (peer_io->readBufferStartsWith(HandshakeName)) // unencrypted
{
if (handshake->encryption_mode == TR_ENCRYPTION_REQUIRED)
{
@@ -710,14 +700,13 @@ static ReadState readPadA(tr_handshake* handshake, tr_peerIo* peer_io)
for (size_t i = 0; i < PadaMaxlen; ++i)
{
auto const* const peek = peer_io->peek(std::size(needle));
if (peek == nullptr)
if (peer_io->readBufferSize() < std::size(needle))
{
tr_logAddTraceHand(handshake, "not enough bytes... returning read_more");
return READ_LATER;
}
if (std::equal(std::begin(needle), std::end(needle), peek))
if (peer_io->readBufferStartsWith(needle))
{
tr_logAddTraceHand(handshake, "found it... looking setting to awaiting_crypto_provide");
peer_io->readBufferDrain(std::size(needle));
@@ -839,11 +828,11 @@ static ReadState readIA(tr_handshake* handshake, tr_peerIo* peer_io)
**/
peer_io->encryptInit(peer_io->isIncoming(), handshake->dh, *peer_io->torrentHash());
evbuffer* const outbuf = evbuffer_new();
auto outbuf = libtransmission::Buffer{};
// send VC
tr_logAddTraceHand(handshake, "sending vc");
evbuffer_add(outbuf, std::data(VC), std::size(VC));
outbuf.add(VC);
/* send crypto_select */
uint32_t const crypto_select = getCryptoSelect(handshake->encryption_mode, handshake->crypto_provide);
@@ -851,12 +840,11 @@ static ReadState readIA(tr_handshake* handshake, tr_peerIo* peer_io)
if (crypto_select != 0)
{
tr_logAddTraceHand(handshake, fmt::format("selecting crypto mode '{}'", crypto_select));
evbuffer_add_uint32(outbuf, crypto_select);
outbuf.addUint32(crypto_select);
}
else
{
tr_logAddTraceHand(handshake, "peer didn't offer an encryption mode we like.");
evbuffer_free(outbuf);
return tr_handshakeDone(handshake, false);
}
@@ -865,15 +853,13 @@ static ReadState readIA(tr_handshake* handshake, tr_peerIo* peer_io)
/* ENCRYPT(VC, crypto_provide, len(PadD), PadD
* PadD is reserved for future extensions to the handshake...
* standard practice at this time is for it to be zero-length */
{
uint16_t const len = 0;
evbuffer_add_uint16(outbuf, len);
}
outbuf.addUint16(0);
/* maybe de-encrypt our connection */
if (crypto_select == CryptoProvidePlaintext)
{
peer_io->writeBuf(outbuf, false);
peer_io->write(outbuf, false);
TR_ASSERT(std::empty(outbuf));
}
tr_logAddTraceHand(handshake, "sending handshake");
@@ -881,7 +867,7 @@ static ReadState readIA(tr_handshake* handshake, tr_peerIo* peer_io)
/* send our handshake */
if (auto msg = std::array<uint8_t, HandshakeSize>{}; buildHandshakeMessage(handshake, std::data(msg)))
{
evbuffer_add(outbuf, std::data(msg), std::size(msg));
outbuf.add(msg);
handshake->haveSentBitTorrentHandshake = true;
}
else
@@ -890,8 +876,7 @@ static ReadState readIA(tr_handshake* handshake, tr_peerIo* peer_io)
}
/* send it out */
peer_io->writeBuf(outbuf, false);
evbuffer_free(outbuf);
peer_io->write(outbuf, false);
/* now await the handshake */
setState(handshake, AWAITING_PAYLOAD_STREAM);

View File

@@ -131,9 +131,9 @@ static void canReadWrapper(tr_peerIo* io_in)
while (!done && !err)
{
size_t piece = 0;
size_t const old_len = evbuffer_get_length(io->inbuf.get());
size_t const old_len = io->readBufferSize();
int const ret = io->canRead(io.get(), io->userData, &piece);
size_t const used = old_len - evbuffer_get_length(io->inbuf.get());
size_t const used = old_len - io->readBufferSize();
unsigned int const overhead = guessPacketOverhead(used);
if (piece != 0 || piece != used)
@@ -157,7 +157,7 @@ static void canReadWrapper(tr_peerIo* io_in)
switch (ret)
{
case READ_NOW:
if (evbuffer_get_length(io->inbuf.get()) != 0)
if (io->readBufferSize() != 0)
{
continue;
}
@@ -190,7 +190,7 @@ static void event_read_cb(evutil_socket_t fd, short /*event*/, void* vio)
io->pendingEvents &= ~EV_READ;
unsigned int const curlen = evbuffer_get_length(io->inbuf.get());
unsigned int const curlen = io->readBufferSize();
unsigned int howmuch = curlen >= max ? 0 : max - curlen;
howmuch = io->bandwidth().clamp(TR_DOWN, howmuch);
@@ -203,10 +203,8 @@ static void event_read_cb(evutil_socket_t fd, short /*event*/, void* vio)
return;
}
EVUTIL_SET_SOCKET_ERROR(0);
auto const res = evbuffer_read(io->inbuf.get(), fd, (int)howmuch);
int const e = EVUTIL_SOCKET_ERROR();
tr_error* error = nullptr;
auto const res = io->inbuf.addSocket(fd, howmuch, &error);
if (res > 0)
{
io->setEnabled(dir, true);
@@ -222,36 +220,33 @@ static void event_read_cb(evutil_socket_t fd, short /*event*/, void* vio)
{
what |= BEV_EVENT_EOF;
}
else if (res == -1)
if (error != nullptr)
{
if (e == EAGAIN || e == EINTR)
if (error->code == EAGAIN || error->code == EINTR)
{
io->setEnabled(dir, true);
return;
}
what |= BEV_EVENT_ERROR;
}
tr_logAddDebugIo(
io,
fmt::format("event_read_cb err: res:{}, what:{}, errno:{} ({})", res, what, e, tr_net_strerror(e)));
tr_logAddDebugIo(
io,
fmt::format("event_read_cb err: res:{}, what:{}, errno:{} ({})", res, what, error->code, error->message));
}
if (io->gotError != nullptr)
{
io->gotError(io, what, io->userData);
}
}
tr_error_clear(&error);
}
static int tr_evbuffer_write(tr_peerIo* io, int fd, size_t howmuch)
{
EVUTIL_SET_SOCKET_ERROR(0);
int const n = evbuffer_write_atmost(io->outbuf.get(), fd, howmuch);
int const e = EVUTIL_SOCKET_ERROR();
tr_logAddTraceIo(io, fmt::format("wrote {} to peer ({})", n, (n == -1 ? tr_net_strerror(e).c_str() : "")));
return n;
return io->outbuf.toSocket(fd, howmuch);
}
static void event_write_cb(evutil_socket_t fd, short /*event*/, void* vio)
@@ -265,10 +260,10 @@ static void event_write_cb(evutil_socket_t fd, short /*event*/, void* vio)
tr_logAddTraceIo(io, "libevent says this peer is ready to write");
// Write as much as possible. Since the socket is non-blocking, write()
// will return if it can't write any more data without blocking.
/* Write as much as possible, since the socket is non-blocking, write() will
* return if it can't write any more data without blocking */
auto constexpr Dir = TR_UP;
size_t const howmuch = io->bandwidth().clamp(Dir, evbuffer_get_length(io->outbuf.get()));
auto const howmuch = io->bandwidth().clamp(Dir, std::size(io->outbuf));
// if we don't have any bandwidth left, stop writing
if (howmuch < 1)
@@ -283,7 +278,7 @@ static void event_write_cb(evutil_socket_t fd, short /*event*/, void* vio)
auto const should_retry = n_written == -1 && (err == 0 || err == EAGAIN || err == EINTR || err == EINPROGRESS);
// schedule another write if we have more data to write & think future writes would succeed
if ((evbuffer_get_length(io->outbuf.get()) != 0) && (n_written > 0 || should_retry))
if (!std::empty(io->outbuf) && (n_written > 0 || should_retry))
{
io->setEnabled(Dir, true);
}
@@ -323,12 +318,7 @@ static void maybeSetCongestionAlgorithm(tr_socket_t socket, std::string const& a
void tr_peerIo::readBufferAdd(void const* data, size_t n_bytes)
{
if (auto const rc = evbuffer_add(inbuf.get(), data, n_bytes); rc < 0)
{
tr_logAddWarn(_("Couldn't write to peer"));
return;
}
inbuf.add(data, n_bytes);
setEnabled(TR_DOWN, true);
canReadWrapper(this);
}
@@ -348,7 +338,7 @@ static void utp_on_writable(tr_peerIo* io)
tr_logAddTraceIo(io, "libutp says this peer is ready to write");
int const n = tr_peerIoTryWrite(io, SIZE_MAX);
io->setEnabled(TR_UP, n != 0 && evbuffer_get_length(io->outbuf.get()) != 0);
io->setEnabled(TR_UP, n != 0 && !std::empty(io->outbuf));
}
static void utp_on_state_change(tr_peerIo* const io, int const state)
@@ -807,7 +797,7 @@ static unsigned int getDesiredOutputBufferSize(tr_peerIo const* io, uint64_t now
size_t tr_peerIo::getWriteBufferSpace(uint64_t now) const
{
size_t const desired_len = getDesiredOutputBufferSize(this, now);
size_t const current_len = evbuffer_get_length(outbuf.get());
size_t const current_len = std::size(outbuf);
return desired_len > current_len ? desired_len - current_len : 0U;
}
@@ -815,57 +805,33 @@ size_t tr_peerIo::getWriteBufferSpace(uint64_t now) const
***
**/
static inline void processBuffer(tr_peerIo& io, evbuffer* buffer, size_t offset, size_t size)
void tr_peerIo::write(libtransmission::Buffer& buf, bool is_piece_data)
{
struct evbuffer_ptr pos = {};
auto iov = evbuffer_iovec{};
auto const n_bytes = std::size(buf);
evbuffer_ptr_set(buffer, &pos, offset, EVBUFFER_PTR_SET);
do
auto const old_size = std::size(outbuf);
outbuf.add(buf);
for (auto iter = std::begin(outbuf) + old_size, end = std::end(outbuf); iter != end; ++iter)
{
if (evbuffer_peek(buffer, size, &pos, &iov, 1) <= 0)
{
break;
}
io.encrypt(iov.iov_len, iov.iov_base);
TR_ASSERT(size >= iov.iov_len);
size -= iov.iov_len;
} while (evbuffer_ptr_set(buffer, &pos, iov.iov_len, EVBUFFER_PTR_ADD) == 0);
TR_ASSERT(size == 0);
}
void tr_peerIo::writeBuf(struct evbuffer* buf, bool is_piece_data)
{
size_t const n_bytes = evbuffer_get_length(buf);
if (isEncrypted())
{
processBuffer(*this, buf, 0, n_bytes);
encrypt(1, &*iter);
}
evbuffer_add_buffer(outbuf.get(), buf);
outbuf_info.emplace_back(n_bytes, is_piece_data);
}
void tr_peerIo::writeBytes(void const* writeme, size_t writeme_len, bool is_piece_data)
void tr_peerIo::writeBytes(void const* bytes, size_t n_bytes, bool is_piece_data)
{
auto iov = evbuffer_iovec{};
evbuffer_reserve_space(outbuf.get(), writeme_len, &iov, 1);
iov.iov_len = writeme_len;
memcpy(iov.iov_base, writeme, iov.iov_len);
auto const old_size = std::size(outbuf);
if (isEncrypted())
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(iov.iov_len, iov.iov_base);
encrypt(1, &*iter);
}
evbuffer_commit_space(outbuf.get(), &iov, 1);
outbuf_info.emplace_back(writeme_len, is_piece_data);
outbuf_info.emplace_back(n_bytes, is_piece_data);
}
/***
@@ -918,7 +884,7 @@ void tr_peerIo::readBytes(void* bytes, size_t byte_count)
{
TR_ASSERT(readBufferSize() >= byte_count);
evbuffer_remove(inbuf.get(), bytes, byte_count);
inbuf.toBuf(bytes, byte_count);
if (isEncrypted())
{
@@ -971,7 +937,7 @@ static int tr_peerIoTryRead(tr_peerIo* io, size_t howmuch)
/* UTP_RBDrained notifies libutp that your read buffer is empty.
* It opens up the congestion window by sending an ACK (soonish)
* if one was not going to be sent. */
if (evbuffer_get_length(io->inbuf.get()) == 0)
if (io->readBufferSize() == 0)
{
utp_read_drained(io->socket.handle.utp);
}
@@ -980,31 +946,38 @@ static int tr_peerIoTryRead(tr_peerIo* io, size_t howmuch)
case TR_PEER_SOCKET_TYPE_TCP:
{
EVUTIL_SET_SOCKET_ERROR(0);
res = evbuffer_read(io->inbuf.get(), io->socket.handle.tcp, (int)howmuch);
int const e = EVUTIL_SOCKET_ERROR();
tr_error* error = nullptr;
res = io->inbuf.addSocket(io->socket.handle.tcp, howmuch, &error);
tr_logAddTraceIo(io, fmt::format("read {} from peer ({})", res, res == -1 ? tr_net_strerror(e).c_str() : ""));
if (evbuffer_get_length(io->inbuf.get()) != 0)
if (io->readBufferSize() != 0)
{
canReadWrapper(io);
}
if (res <= 0 && io->gotError != nullptr && e != EAGAIN && e != EINTR && e != EINPROGRESS)
if (error != nullptr)
{
short what = BEV_EVENT_READING | BEV_EVENT_ERROR;
if (res == 0)
if (error->code != EAGAIN && error->code != EINTR && error->code != EINPROGRESS && io->gotError != nullptr)
{
what |= BEV_EVENT_EOF;
short what = BEV_EVENT_READING | BEV_EVENT_ERROR;
if (res == 0)
{
what |= BEV_EVENT_EOF;
}
tr_logAddTraceIo(
io,
fmt::format(
"tr_peerIoTryRead err: res:{} what:{}, errno:{} ({})",
res,
what,
error->code,
error->message));
io->gotError(io, what, io->userData);
}
tr_logAddTraceIo(
io,
fmt::format("tr_peerIoTryRead err: res:{} what:{}, errno:{} ({})", res, what, e, tr_net_strerror(e)));
io->gotError(io, what, io->userData);
tr_error_clear(&error);
}
break;
@@ -1019,7 +992,7 @@ static int tr_peerIoTryRead(tr_peerIo* io, size_t howmuch)
static int tr_peerIoTryWrite(tr_peerIo* io, size_t howmuch)
{
auto const old_len = size_t{ evbuffer_get_length(io->outbuf.get()) };
auto const old_len = std::size(io->outbuf);
tr_logAddTraceIo(io, fmt::format("in tr_peerIoTryWrite {}", howmuch));
howmuch = std::min(howmuch, old_len);
@@ -1033,16 +1006,17 @@ static int tr_peerIoTryWrite(tr_peerIo* io, size_t howmuch)
switch (io->socket.type)
{
case TR_PEER_SOCKET_TYPE_UTP:
n = utp_write(io->socket.handle.utp, evbuffer_pullup(io->outbuf.get(), howmuch), howmuch);
if (n > 0)
{
evbuffer_drain(io->outbuf.get(), n);
didWriteWrapper(io, n);
auto iov = io->outbuf.vecs(howmuch);
n = utp_writev(io->socket.handle.utp, reinterpret_cast<struct utp_iovec*>(std::data(iov)), std::size(iov));
if (n > 0)
{
io->outbuf.drain(n);
didWriteWrapper(io, n);
}
break;
}
break;
case TR_PEER_SOCKET_TYPE_TCP:
{
EVUTIL_SET_SOCKET_ERROR(0);

View File

@@ -31,6 +31,7 @@
#include "peer-mse.h"
#include "peer-socket.h"
#include "tr-assert.h"
#include "tr-buffer.h"
class tr_peerIo;
struct tr_bandwidth;
@@ -128,26 +129,17 @@ public:
std::string addrStr() const;
[[nodiscard]] auto readBuffer() noexcept
{
return inbuf.get();
}
void readBufferDrain(size_t byte_count);
[[nodiscard]] auto readBufferSize() const noexcept
{
return evbuffer_get_length(inbuf.get());
return std::size(inbuf);
}
[[nodiscard]] std::byte const* peek(size_t n_bytes) const noexcept
template<typename T>
[[nodiscard]] auto readBufferStartsWith(T const& t) const noexcept
{
if (readBufferSize() < n_bytes)
{
return nullptr;
}
return reinterpret_cast<std::byte const*>(evbuffer_pullup(inbuf.get(), n_bytes));
return inbuf.startsWith(t);
}
void readBufferAdd(void const* data, size_t n_bytes);
@@ -155,8 +147,12 @@ public:
int flushOutgoingProtocolMsgs();
int flush(tr_direction dir, size_t byte_limit);
void writeBytes(void const* writeme, size_t writeme_len, bool is_piece_data);
void writeBuf(struct evbuffer* buf, bool is_piece_data);
void writeBytes(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::Buffer& buf, bool is_piece_data);
size_t getWriteBufferSpace(uint64_t now) const;
[[nodiscard]] auto hasBandwidthLeft(tr_direction dir) noexcept
@@ -252,8 +248,8 @@ public:
tr_net_error_cb gotError = nullptr;
void* userData = nullptr;
tr_evbuffer_ptr const inbuf = tr_evbuffer_ptr{ evbuffer_new() };
tr_evbuffer_ptr const outbuf = tr_evbuffer_ptr{ evbuffer_new() };
libtransmission::Buffer inbuf;
libtransmission::Buffer outbuf;
std::deque<std::pair<size_t /*n_bytes*/, bool /*is_piece_data*/>> outbuf_info;

View File

@@ -16,7 +16,6 @@
#include <utility>
#include <vector>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <fmt/format.h>
@@ -24,8 +23,8 @@
#include "transmission.h"
#include "cache.h"
#include "crypto-utils.h"
#include "completion.h"
#include "crypto-utils.h"
#include "file.h"
#include "log.h"
#include "peer-io.h"
@@ -37,6 +36,8 @@
#include "torrent-magnet.h"
#include "torrent.h"
#include "tr-assert.h"
#include "tr-buffer.h"
#include "tr-dht.h"
#include "utils.h"
#include "variant.h"
#include "version.h"
@@ -256,7 +257,6 @@ public:
: tr_peerMsgs{ torrent_in, atom_in }
, outMessagesBatchPeriod{ LowPriorityIntervalSecs }
, torrent{ torrent_in }
, outMessages{ evbuffer_new() }
, io{ std::move(io_in) }
, have_{ torrent_in->pieceCount() }
, callback_{ callback }
@@ -308,13 +308,11 @@ public:
{
this->io->clear();
}
evbuffer_free(this->outMessages);
}
void dbgOutMessageLen() const
{
logtrace(this, fmt::format(FMT_STRING("outMessage size is now {:d}"), evbuffer_get_length(outMessages)));
logtrace(this, fmt::format(FMT_STRING("outMessage size is now {:d}"), std::size(outMessages)));
}
void pokeBatchPeriod(int interval)
@@ -608,12 +606,12 @@ private:
{
TR_ASSERT(isValidRequest(req));
auto* const out = outMessages;
evbuffer_add_uint32(out, sizeof(uint8_t) + 3 * sizeof(uint32_t));
evbuffer_add_uint8(out, BtPeerMsgs::Request);
evbuffer_add_uint32(out, req.index);
evbuffer_add_uint32(out, req.offset);
evbuffer_add_uint32(out, req.length);
auto& out = outMessages;
out.addUint32(sizeof(uint8_t) + 3 * sizeof(uint32_t));
out.addUint8(BtPeerMsgs::Request);
out.addUint32(req.index);
out.addUint32(req.offset);
out.addUint32(req.length);
logtrace(this, fmt::format(FMT_STRING("requesting {:d}:{:d}->{:d}..."), req.index, req.offset, req.length));
dbgOutMessageLen();
@@ -706,7 +704,7 @@ public:
tr_torrent* const torrent;
evbuffer* const outMessages; /* all the non-piece messages */
libtransmission::Buffer outMessages; /* all the non-piece messages */
std::shared_ptr<tr_peerIo> const io;
@@ -774,13 +772,13 @@ static void protocolSendReject(tr_peerMsgsImpl* msgs, struct peer_request const*
{
TR_ASSERT(msgs->io->supportsFEXT());
struct evbuffer* out = msgs->outMessages;
auto& out = msgs->outMessages;
evbuffer_add_uint32(out, sizeof(uint8_t) + 3 * sizeof(uint32_t));
evbuffer_add_uint8(out, BtPeerMsgs::FextReject);
evbuffer_add_uint32(out, req->index);
evbuffer_add_uint32(out, req->offset);
evbuffer_add_uint32(out, req->length);
out.addUint32(sizeof(uint8_t) + 3 * sizeof(uint32_t));
out.addUint8(BtPeerMsgs::FextReject);
out.addUint32(req->index);
out.addUint32(req->offset);
out.addUint32(req->length);
logtrace(msgs, fmt::format(FMT_STRING("rejecting {:d}:{:d}->{:d}..."), req->index, req->offset, req->length));
msgs->dbgOutMessageLen();
@@ -788,13 +786,13 @@ static void protocolSendReject(tr_peerMsgsImpl* msgs, struct peer_request const*
static void protocolSendCancel(tr_peerMsgsImpl* msgs, peer_request const& req)
{
struct evbuffer* out = msgs->outMessages;
auto& out = msgs->outMessages;
evbuffer_add_uint32(out, sizeof(uint8_t) + 3 * sizeof(uint32_t));
evbuffer_add_uint8(out, BtPeerMsgs::Cancel);
evbuffer_add_uint32(out, req.index);
evbuffer_add_uint32(out, req.offset);
evbuffer_add_uint32(out, req.length);
out.addUint32(sizeof(uint8_t) + 3 * sizeof(uint32_t));
out.addUint8(BtPeerMsgs::Cancel);
out.addUint32(req.index);
out.addUint32(req.offset);
out.addUint32(req.length);
logtrace(msgs, fmt::format(FMT_STRING("cancelling {:d}:{:d}->{:d}..."), req.index, req.offset, req.length));
msgs->dbgOutMessageLen();
@@ -803,21 +801,21 @@ static void protocolSendCancel(tr_peerMsgsImpl* msgs, peer_request const& req)
static void protocolSendPort(tr_peerMsgsImpl* msgs, tr_port port)
{
struct evbuffer* out = msgs->outMessages;
auto& out = msgs->outMessages;
logtrace(msgs, fmt::format(FMT_STRING("sending Port {:d}"), port.host()));
evbuffer_add_uint32(out, 3);
evbuffer_add_uint8(out, BtPeerMsgs::Port);
evbuffer_add_uint16(out, port.network());
out.addUint32(3);
out.addUint8(BtPeerMsgs::Port);
out.addUint16(port.network());
}
static void protocolSendHave(tr_peerMsgsImpl* msgs, tr_piece_index_t index)
{
struct evbuffer* out = msgs->outMessages;
auto& out = msgs->outMessages;
evbuffer_add_uint32(out, sizeof(uint8_t) + sizeof(uint32_t));
evbuffer_add_uint8(out, BtPeerMsgs::Have);
evbuffer_add_uint32(out, index);
out.addUint32(sizeof(uint8_t) + sizeof(uint32_t));
out.addUint8(BtPeerMsgs::Have);
out.addUint32(index);
logtrace(msgs, fmt::format(FMT_STRING("sending Have {:d}"), index));
msgs->dbgOutMessageLen();
@@ -826,10 +824,10 @@ static void protocolSendHave(tr_peerMsgsImpl* msgs, tr_piece_index_t index)
static void protocolSendChoke(tr_peerMsgsImpl* msgs, bool choke)
{
struct evbuffer* out = msgs->outMessages;
auto& out = msgs->outMessages;
evbuffer_add_uint32(out, sizeof(uint8_t));
evbuffer_add_uint8(out, choke ? BtPeerMsgs::Choke : BtPeerMsgs::Unchoke);
out.addUint32(sizeof(uint8_t));
out.addUint8(choke ? BtPeerMsgs::Choke : BtPeerMsgs::Unchoke);
logtrace(msgs, choke ? "sending choke" : "sending unchoked");
msgs->dbgOutMessageLen();
@@ -840,10 +838,10 @@ static void protocolSendHaveAll(tr_peerMsgsImpl* msgs)
{
TR_ASSERT(msgs->io->supportsFEXT());
struct evbuffer* out = msgs->outMessages;
auto& out = msgs->outMessages;
evbuffer_add_uint32(out, sizeof(uint8_t));
evbuffer_add_uint8(out, BtPeerMsgs::FextHaveAll);
out.addUint32(sizeof(uint8_t));
out.addUint8(BtPeerMsgs::FextHaveAll);
logtrace(msgs, "sending HAVE_ALL...");
msgs->dbgOutMessageLen();
@@ -854,10 +852,10 @@ static void protocolSendHaveNone(tr_peerMsgsImpl* msgs)
{
TR_ASSERT(msgs->io->supportsFEXT());
struct evbuffer* out = msgs->outMessages;
auto& out = msgs->outMessages;
evbuffer_add_uint32(out, sizeof(uint8_t));
evbuffer_add_uint8(out, BtPeerMsgs::FextHaveNone);
out.addUint32(sizeof(uint8_t));
out.addUint8(BtPeerMsgs::FextHaveNone);
logtrace(msgs, "sending HAVE_NONE...");
msgs->dbgOutMessageLen();
@@ -872,11 +870,11 @@ static void sendInterest(tr_peerMsgsImpl* msgs, bool b)
{
TR_ASSERT(msgs != nullptr);
struct evbuffer* out = msgs->outMessages;
auto& out = msgs->outMessages;
logtrace(msgs, b ? "Sending Interested" : "Sending Not Interested");
evbuffer_add_uint32(out, sizeof(uint8_t));
evbuffer_add_uint8(out, b ? BtPeerMsgs::Interested : BtPeerMsgs::NotInterested);
out.addUint32(sizeof(uint8_t));
out.addUint8(b ? BtPeerMsgs::Interested : BtPeerMsgs::NotInterested);
msgs->pokeBatchPeriod(HighPriorityIntervalSecs);
msgs->dbgOutMessageLen();
@@ -914,7 +912,7 @@ static void cancelAllRequestsToClient(tr_peerMsgsImpl* msgs)
static void sendLtepHandshake(tr_peerMsgsImpl* msgs)
{
evbuffer* const out = msgs->outMessages;
auto& out = msgs->outMessages;
auto const ipv6 = tr_globalIPv6(msgs->io->session);
static tr_quark version_quark = 0;
@@ -1009,17 +1007,16 @@ static void sendLtepHandshake(tr_peerMsgsImpl* msgs)
}
}
auto* const payload = tr_variantToBuf(&val, TR_VARIANT_FMT_BENC);
auto payload = tr_variantToStr(&val, TR_VARIANT_FMT_BENC);
evbuffer_add_uint32(out, 2 * sizeof(uint8_t) + evbuffer_get_length(payload));
evbuffer_add_uint8(out, BtPeerMsgs::Ltep);
evbuffer_add_uint8(out, LtepMessages::Handshake);
evbuffer_add_buffer(out, payload);
out.addUint32(2 * sizeof(uint8_t) + std::size(payload));
out.addUint8(BtPeerMsgs::Ltep);
out.addUint8(LtepMessages::Handshake);
out.add(payload);
msgs->pokeBatchPeriod(ImmediatePriorityIntervalSecs);
msgs->dbgOutMessageLen();
/* cleanup */
evbuffer_free(payload);
tr_variantClear(&val);
}
@@ -1175,25 +1172,24 @@ static void parseUtMetadata(tr_peerMsgsImpl* msgs, uint32_t msglen)
}
else
{
evbuffer* const out = msgs->outMessages;
auto& out = msgs->outMessages;
/* build the rejection message */
auto v = tr_variant{};
tr_variantInitDict(&v, 2);
tr_variantDictAddInt(&v, TR_KEY_msg_type, MetadataMsgType::Reject);
tr_variantDictAddInt(&v, TR_KEY_piece, piece);
evbuffer* const payload = tr_variantToBuf(&v, TR_VARIANT_FMT_BENC);
auto const payload = tr_variantToStr(&v, TR_VARIANT_FMT_BENC);
/* write it out as a LTEP message to our outMessages buffer */
evbuffer_add_uint32(out, 2 * sizeof(uint8_t) + evbuffer_get_length(payload));
evbuffer_add_uint8(out, BtPeerMsgs::Ltep);
evbuffer_add_uint8(out, msgs->ut_metadata_id);
evbuffer_add_buffer(out, payload);
out.addUint32(2 * sizeof(uint8_t) + std::size(payload));
out.addUint8(BtPeerMsgs::Ltep);
out.addUint8(msgs->ut_metadata_id);
out.add(payload);
msgs->pokeBatchPeriod(HighPriorityIntervalSecs);
msgs->dbgOutMessageLen();
/* cleanup */
evbuffer_free(payload);
tr_variantClear(&v);
}
}
@@ -1930,27 +1926,26 @@ static void updateMetadataRequests(tr_peerMsgsImpl* msgs, time_t now)
if (auto const piece = tr_torrentGetNextMetadataRequest(msgs->torrent, now); piece)
{
evbuffer* const out = msgs->outMessages;
auto& out = msgs->outMessages;
/* build the data message */
auto tmp = tr_variant{};
tr_variantInitDict(&tmp, 3);
tr_variantDictAddInt(&tmp, TR_KEY_msg_type, MetadataMsgType::Request);
tr_variantDictAddInt(&tmp, TR_KEY_piece, *piece);
auto* const payload = tr_variantToBuf(&tmp, TR_VARIANT_FMT_BENC);
auto const payload = tr_variantToStr(&tmp, TR_VARIANT_FMT_BENC);
logtrace(msgs, fmt::format(FMT_STRING("requesting metadata piece #{:d}"), *piece));
/* write it out as a LTEP message to our outMessages buffer */
evbuffer_add_uint32(out, 2 * sizeof(uint8_t) + evbuffer_get_length(payload));
evbuffer_add_uint8(out, BtPeerMsgs::Ltep);
evbuffer_add_uint8(out, msgs->ut_metadata_id);
evbuffer_add_buffer(out, payload);
out.addUint32(2 * sizeof(uint8_t) + std::size(payload));
out.addUint8(BtPeerMsgs::Ltep);
out.addUint8(msgs->ut_metadata_id);
out.add(payload);
msgs->pokeBatchPeriod(HighPriorityIntervalSecs);
msgs->dbgOutMessageLen();
/* cleanup */
evbuffer_free(payload);
tr_variantClear(&tmp);
}
}
@@ -1989,7 +1984,7 @@ static size_t fillOutputBuffer(tr_peerMsgsImpl* msgs, time_t now)
{
size_t bytes_written = 0;
struct peer_request req;
bool const have_messages = evbuffer_get_length(msgs->outMessages) != 0;
bool const have_messages = !std::empty(msgs->outMessages);
bool const fext = msgs->io->supportsFEXT();
/**
@@ -1998,17 +1993,15 @@ static size_t fillOutputBuffer(tr_peerMsgsImpl* msgs, time_t now)
if (have_messages && msgs->outMessagesBatchedAt == 0) /* fresh batch */
{
logtrace(
msgs,
fmt::format(FMT_STRING("started an outMessages batch (length is {:d})"), evbuffer_get_length(msgs->outMessages)));
logtrace(msgs, fmt::format(FMT_STRING("started an outMessages batch (length is {:d})"), std::size(msgs->outMessages)));
msgs->outMessagesBatchedAt = now;
}
else if (have_messages && now - msgs->outMessagesBatchedAt >= msgs->outMessagesBatchPeriod)
{
size_t const len = evbuffer_get_length(msgs->outMessages);
auto const len = std::size(msgs->outMessages);
/* flush the protocol messages */
logtrace(msgs, fmt::format(FMT_STRING("flushing outMessages... to {:p} (length is {:d})"), fmt::ptr(msgs->io), len));
msgs->io->writeBuf(msgs->outMessages, false);
msgs->io->write(msgs->outMessages, false);
msgs->clientSentAnythingAt = now;
msgs->outMessagesBatchedAt = 0;
msgs->outMessagesBatchPeriod = LowPriorityIntervalSecs;
@@ -2026,7 +2019,7 @@ static size_t fillOutputBuffer(tr_peerMsgsImpl* msgs, time_t now)
if (auto const piece_data = tr_torrentGetMetadataPiece(msgs->torrent, piece); piece_data)
{
auto* const out = msgs->outMessages;
auto& out = msgs->outMessages;
/* build the data message */
auto tmp = tr_variant{};
@@ -2034,18 +2027,17 @@ static size_t fillOutputBuffer(tr_peerMsgsImpl* msgs, time_t now)
tr_variantDictAddInt(&tmp, TR_KEY_msg_type, MetadataMsgType::Data);
tr_variantDictAddInt(&tmp, TR_KEY_piece, piece);
tr_variantDictAddInt(&tmp, TR_KEY_total_size, msgs->torrent->infoDictSize());
evbuffer* const payload = tr_variantToBuf(&tmp, TR_VARIANT_FMT_BENC);
auto const payload = tr_variantToStr(&tmp, TR_VARIANT_FMT_BENC);
/* write it out as a LTEP message to our outMessages buffer */
evbuffer_add_uint32(out, 2 * sizeof(uint8_t) + evbuffer_get_length(payload) + std::size(*piece_data));
evbuffer_add_uint8(out, BtPeerMsgs::Ltep);
evbuffer_add_uint8(out, msgs->ut_metadata_id);
evbuffer_add_buffer(out, payload);
evbuffer_add(out, std::data(*piece_data), std::size(*piece_data));
out.addUint32(2 * sizeof(uint8_t) + std::size(payload) + std::size(*piece_data));
out.addUint8(BtPeerMsgs::Ltep);
out.addUint8(msgs->ut_metadata_id);
out.add(payload);
out.add(*piece_data);
msgs->pokeBatchPeriod(HighPriorityIntervalSecs);
msgs->dbgOutMessageLen();
evbuffer_free(payload);
tr_variantClear(&tmp);
ok = true;
@@ -2053,24 +2045,23 @@ static size_t fillOutputBuffer(tr_peerMsgsImpl* msgs, time_t now)
if (!ok) /* send a rejection message */
{
evbuffer* const out = msgs->outMessages;
auto& out = msgs->outMessages;
/* build the rejection message */
auto tmp = tr_variant{};
tr_variantInitDict(&tmp, 2);
tr_variantDictAddInt(&tmp, TR_KEY_msg_type, MetadataMsgType::Reject);
tr_variantDictAddInt(&tmp, TR_KEY_piece, piece);
evbuffer* const payload = tr_variantToBuf(&tmp, TR_VARIANT_FMT_BENC);
auto payload = tr_variantToStr(&tmp, TR_VARIANT_FMT_BENC);
/* write it out as a LTEP message to our outMessages buffer */
evbuffer_add_uint32(out, 2 * sizeof(uint8_t) + evbuffer_get_length(payload));
evbuffer_add_uint8(out, BtPeerMsgs::Ltep);
evbuffer_add_uint8(out, msgs->ut_metadata_id);
evbuffer_add_buffer(out, payload);
out.addUint32(2 * sizeof(uint8_t) + std::size(payload));
out.addUint8(BtPeerMsgs::Ltep);
out.addUint8(msgs->ut_metadata_id);
out.add(payload);
msgs->pokeBatchPeriod(HighPriorityIntervalSecs);
msgs->dbgOutMessageLen();
evbuffer_free(payload);
tr_variantClear(&tmp);
}
}
@@ -2087,24 +2078,21 @@ static size_t fillOutputBuffer(tr_peerMsgsImpl* msgs, time_t now)
if (msgs->isValidRequest(req) && msgs->torrent->hasPiece(req.index))
{
uint32_t const msglen = 4 + 1 + 4 + 4 + req.length;
auto iov = evbuffer_iovec{};
auto* const out = evbuffer_new();
evbuffer_expand(out, msglen);
auto out = libtransmission::Buffer{};
out.reserve(msglen);
evbuffer_add_uint32(out, sizeof(uint8_t) + 2 * sizeof(uint32_t) + req.length);
evbuffer_add_uint8(out, BtPeerMsgs::Piece);
evbuffer_add_uint32(out, req.index);
evbuffer_add_uint32(out, req.offset);
evbuffer_reserve_space(out, req.length, &iov, 1);
out.addUint32(sizeof(uint8_t) + 2 * sizeof(uint32_t) + req.length);
out.addUint8(BtPeerMsgs::Piece);
out.addUint32(req.index);
out.addUint32(req.offset);
auto buf = std::array<uint8_t, tr_block_info::BlockSize>{};
bool err = msgs->session->cache->readBlock(
msgs->torrent,
msgs->torrent->pieceLoc(req.index, req.offset),
req.length,
static_cast<uint8_t*>(iov.iov_base)) != 0;
iov.iov_len = req.length;
evbuffer_commit_space(out, &iov, 1);
std::data(buf)) != 0;
out.add(std::data(buf), req.length);
/* check the piece if it needs checking... */
if (!err)
@@ -2126,17 +2114,15 @@ static size_t fillOutputBuffer(tr_peerMsgsImpl* msgs, time_t now)
}
else
{
size_t const n = evbuffer_get_length(out);
logtrace(msgs, fmt::format(FMT_STRING("sending block {:d}:{:d}->{:d}"), req.index, req.offset, req.length));
auto const n = std::size(out);
TR_ASSERT(n == msglen);
msgs->io->writeBuf(out, true);
msgs->io->write(out, true);
bytes_written += n;
msgs->clientSentAnythingAt = now;
msgs->blocks_sent_to_peer.add(tr_time(), 1);
}
evbuffer_free(out);
if (err)
{
bytes_written = 0;
@@ -2161,7 +2147,7 @@ static size_t fillOutputBuffer(tr_peerMsgsImpl* msgs, time_t now)
if (msgs != nullptr && msgs->clientSentAnythingAt != 0 && now - msgs->clientSentAnythingAt > KeepaliveIntervalSecs)
{
logtrace(msgs, "sending a keepalive message");
evbuffer_add_uint32(msgs->outMessages, 0);
msgs->outMessages.addUint32(0);
msgs->pokeBatchPeriod(ImmediatePriorityIntervalSecs);
}
@@ -2209,13 +2195,13 @@ static void sendBitfield(tr_peerMsgsImpl* msgs)
{
TR_ASSERT(msgs->torrent->hasMetainfo());
struct evbuffer* out = msgs->outMessages;
auto& out = msgs->outMessages;
auto bytes = msgs->torrent->createPieceBitfield();
evbuffer_add_uint32(out, sizeof(uint8_t) + bytes.size());
evbuffer_add_uint8(out, BtPeerMsgs::Bitfield);
evbuffer_add(out, bytes.data(), std::size(bytes));
logtrace(msgs, fmt::format(FMT_STRING("sending bitfield... outMessage size is now {:d}"), evbuffer_get_length(out)));
out.addUint32(sizeof(uint8_t) + bytes.size());
out.addUint8(BtPeerMsgs::Bitfield);
out.add(bytes);
logtrace(msgs, fmt::format(FMT_STRING("sending bitfield... outMessage size is now {:d}"), std::size(out)));
msgs->pokeBatchPeriod(ImmediatePriorityIntervalSecs);
}
@@ -2292,7 +2278,7 @@ void tr_peerMsgsImpl::sendPex()
return;
}
evbuffer* const out = this->outMessages;
auto& out = this->outMessages;
// update msgs
std::swap(old4, new4);
@@ -2403,15 +2389,14 @@ void tr_peerMsgsImpl::sendPex()
}
/* write the pex message */
auto* const payload = tr_variantToBuf(&val, TR_VARIANT_FMT_BENC);
evbuffer_add_uint32(out, 2 * sizeof(uint8_t) + evbuffer_get_length(payload));
evbuffer_add_uint8(out, BtPeerMsgs::Ltep);
evbuffer_add_uint8(out, this->ut_pex_id);
evbuffer_add_buffer(out, payload);
auto payload = tr_variantToStr(&val, TR_VARIANT_FMT_BENC);
out.addUint32(2 * sizeof(uint8_t) + std::size(payload));
out.addUint8(BtPeerMsgs::Ltep);
out.addUint8(this->ut_pex_id);
out.add(payload);
this->pokeBatchPeriod(HighPriorityIntervalSecs);
logtrace(this, fmt::format(FMT_STRING("sending a pex message; outMessage size is now {:d}"), evbuffer_get_length(out)));
logtrace(this, fmt::format(FMT_STRING("sending a pex message; outMessage size is now {:d}"), std::size(out)));
this->dbgOutMessageLen();
evbuffer_free(payload);
tr_variantClear(&val);
}

335
libtransmission/tr-buffer.h Normal file
View File

@@ -0,0 +1,335 @@
// This file Copyright 2022 Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#pragma once
#include <cstddef>
#include <iterator>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include <event2/buffer.h>
#include "error.h"
#include "net.h" // tr_socket_t
#include "utils.h"
namespace libtransmission
{
class Buffer
{
public:
using Iovec = evbuffer_iovec;
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;
Iterator(evbuffer* buf, size_t offset)
: buf_{ buf }
{
setOffset(offset);
}
[[nodiscard]] value_type& operator*() noexcept
{
return *reinterpret_cast<value_type*>(iov_.iov_base);
}
[[nodiscard]] value_type operator*() const noexcept
{
return *reinterpret_cast<value_type*>(iov_.iov_base);
}
[[nodiscard]] Iterator operator+(int n_bytes)
{
return Iterator(buf_, offset_ + n_bytes);
}
Iterator& operator++() noexcept
{
if (iov_.iov_len > 1)
{
iov_.iov_base = reinterpret_cast<value_type*>(iov_.iov_base) + 1;
--iov_.iov_len;
++offset_;
}
else
{
setOffset(offset_ + 1);
}
return *this;
}
[[nodiscard]] constexpr bool operator==(Iterator const& that) const noexcept
{
return offset_ == that.offset_;
}
[[nodiscard]] constexpr bool operator!=(Iterator const& that) const noexcept
{
return !(*this == that);
}
private:
void setOffset(size_t offset)
{
offset_ = offset;
auto ptr = evbuffer_ptr{};
evbuffer_ptr_set(buf_, &ptr, offset, EVBUFFER_PTR_SET);
evbuffer_peek(buf_, std::numeric_limits<ev_ssize_t>::max(), &ptr, &iov_, 1);
}
evbuffer* const buf_;
Iovec iov_ = {};
size_t offset_ = 0;
};
Buffer() = default;
Buffer(Buffer&&) = default;
Buffer(Buffer const&) = delete;
Buffer& operator=(Buffer const&) = delete;
Buffer& operator=(Buffer&&) = default;
template<typename T>
Buffer(T const& data)
{
add(std::data(data), std::size(data));
}
[[nodiscard]] auto size() const noexcept
{
return evbuffer_get_length(buf_.get());
}
[[nodiscard]] auto empty() const noexcept
{
return size() == 0U;
}
[[nodiscard]] auto vecs(size_t n_bytes) const
{
auto chains = std::vector<Iovec>(evbuffer_peek(buf_.get(), n_bytes, nullptr, nullptr, 0));
evbuffer_peek(buf_.get(), n_bytes, nullptr, std::data(chains), std::size(chains));
return chains;
}
[[nodiscard]] auto vecs() const
{
return vecs(size());
}
[[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());
}
[[nodiscard]] auto cbegin() const noexcept
{
return Iterator(buf_.get(), 0U);
}
[[nodiscard]] auto cend() const noexcept
{
return Iterator(buf_.get(), size());
}
template<typename T>
[[nodiscard]] bool startsWith(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());
}
auto toBuf(void* tgt, size_t n_bytes)
{
return evbuffer_remove(buf_.get(), tgt, n_bytes);
}
[[nodiscard]] uint16_t toUint16()
{
auto tmp = uint16_t{};
toBuf(&tmp, sizeof(tmp));
return ntohs(tmp);
}
[[nodiscard]] uint32_t toUint32()
{
auto tmp = uint32_t{};
toBuf(&tmp, sizeof(tmp));
return ntohl(tmp);
}
[[nodiscard]] uint64_t toUint64()
{
auto tmp = uint64_t{};
toBuf(&tmp, sizeof(tmp));
return tr_ntohll(tmp);
}
void drain(size_t n_bytes)
{
evbuffer_drain(buf_.get(), n_bytes);
}
void clear()
{
drain(size());
}
// -1 on error, 0 on eof, >0 on n bytes written
auto toSocket(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 == -1)
{
tr_error_set(error, err, tr_net_strerror(err));
}
return res;
}
[[nodiscard]] Iovec alloc(size_t n_bytes)
{
auto iov = Iovec{};
evbuffer_reserve_space(buf_.get(), static_cast<ev_ssize_t>(n_bytes), &iov, 1);
return iov;
}
void commit(Iovec iov)
{
evbuffer_commit_space(buf_.get(), &iov, 1);
}
void reserve(size_t n_bytes)
{
evbuffer_expand(buf_.get(), n_bytes - size());
}
// -1 on error, 0 on eof, >0 for num bytes read
auto addSocket(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 == -1)
{
tr_error_set(error, err, tr_net_strerror(err));
}
return res;
}
// Move all data from one buffer into another.
// This is a destructive add: the source buffer is empty after this call.
void add(Buffer& that)
{
evbuffer_add_buffer(buf_.get(), that.buf_.get());
}
void add(Buffer&& that)
{
evbuffer_add_buffer(buf_.get(), that.buf_.get());
}
void add(void const* bytes, size_t n_bytes)
{
evbuffer_add(buf_.get(), bytes, n_bytes);
}
template<typename T>
void add(T const& data)
{
add(std::data(data), std::size(data));
}
template<
typename T,
typename std::enable_if_t<
std::is_same_v<T, char> || std::is_same_v<T, unsigned char> || std::is_same_v<T, std::byte>>* = nullptr>
void push_back(T ch)
{
add(&ch, 1);
}
void addUint8(uint8_t uch)
{
add(&uch, 1);
}
void addUint16(uint16_t hs)
{
uint16_t const ns = htons(hs);
add(&ns, sizeof(ns));
}
void addHton16(uint16_t hs)
{
addUint16(hs);
}
void addUint32(uint32_t hl)
{
uint32_t const nl = htonl(hl);
add(&nl, sizeof(nl));
}
void addHton32(uint32_t hl)
{
addUint32(hl);
}
void addUint64(uint64_t hll)
{
uint64_t const nll = tr_htonll(hll);
add(&nll, sizeof(nll));
}
void addHton64(uint64_t hll)
{
addUint64(hll);
}
[[nodiscard]] std::string toString() const
{
auto str = std::string{};
str.reserve(size());
for (auto const& by : *this)
{
str.push_back(*reinterpret_cast<char const*>(&by));
}
return str;
}
private:
std::unique_ptr<evbuffer, void (*)(evbuffer*)> buf_{ evbuffer_new(), evbuffer_free };
};
} // namespace libtransmission

View File

@@ -9,8 +9,6 @@
#include <string_view>
#include <optional>
#include <event2/buffer.h>
#include <fmt/compile.h>
#include <fmt/format.h>
@@ -19,8 +17,9 @@
#include "transmission.h"
#include "benc.h"
#include "tr-assert.h"
#include "quark.h"
#include "tr-assert.h"
#include "tr-buffer.h"
#include "utils.h"
#include "variant-common.h"
#include "variant.h"
@@ -274,70 +273,58 @@ bool tr_variantParseBenc(tr_variant& top, int parse_opts, std::string_view benc,
*****
****/
static void saveIntFunc(tr_variant const* val, void* vevbuf)
using Buffer = libtransmission::Buffer;
static void saveIntFunc(tr_variant const* val, void* vout)
{
auto buf = std::array<char, 64>{};
auto const out = fmt::format_to(std::data(buf), FMT_COMPILE("i{:d}e"), val->val.i);
auto* const evbuf = static_cast<evbuffer*>(vevbuf);
evbuffer_add(evbuf, std::data(buf), static_cast<size_t>(out - std::data(buf)));
static_cast<Buffer*>(vout)->add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
}
static void saveBoolFunc(tr_variant const* val, void* vevbuf)
static void saveBoolFunc(tr_variant const* val, void* vout)
{
auto* const evbuf = static_cast<evbuffer*>(vevbuf);
if (val->val.b)
{
evbuffer_add(evbuf, "i1e", 3);
}
else
{
evbuffer_add(evbuf, "i0e", 3);
}
static_cast<Buffer*>(vout)->add(val->val.b ? "i1e"sv : "i0e"sv);
}
static void saveStringImpl(evbuffer* evbuf, std::string_view sv)
static void saveStringImpl(Buffer* tgt, std::string_view sv)
{
// `${sv.size()}:${sv}`
auto prefix = std::array<char, 32>{};
auto out = fmt::format_to(std::data(prefix), FMT_COMPILE("{:d}:"), std::size(sv));
evbuffer_add(evbuf, std::data(prefix), out - std::data(prefix));
evbuffer_add(evbuf, std::data(sv), std::size(sv));
tgt->add(std::data(prefix), out - std::data(prefix));
tgt->add(sv);
}
static void saveStringFunc(tr_variant const* v, void* vevbuf)
static void saveStringFunc(tr_variant const* v, void* vout)
{
auto sv = std::string_view{};
(void)!tr_variantGetStrView(v, &sv);
auto* const evbuf = static_cast<evbuffer*>(vevbuf);
saveStringImpl(evbuf, sv);
saveStringImpl(static_cast<Buffer*>(vout), sv);
}
static void saveRealFunc(tr_variant const* val, void* vevbuf)
static void saveRealFunc(tr_variant const* val, void* vout)
{
// the benc spec doesn't handle floats; save it as a string.
auto buf = std::array<char, 64>{};
auto out = fmt::format_to(std::data(buf), FMT_COMPILE("{:f}"), val->val.d);
auto* const evbuf = static_cast<evbuffer*>(vevbuf);
saveStringImpl(evbuf, { 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)) });
}
static void saveDictBeginFunc(tr_variant const* /*val*/, void* vevbuf)
static void saveDictBeginFunc(tr_variant const* /*val*/, void* vbuf)
{
auto* const evbuf = static_cast<evbuffer*>(vevbuf);
evbuffer_add(evbuf, "d", 1);
static_cast<Buffer*>(vbuf)->push_back('d');
}
static void saveListBeginFunc(tr_variant const* /*val*/, void* vevbuf)
static void saveListBeginFunc(tr_variant const* /*val*/, void* vbuf)
{
auto* const evbuf = static_cast<evbuffer*>(vevbuf);
evbuffer_add(evbuf, "l", 1);
static_cast<Buffer*>(vbuf)->push_back('l');
}
static void saveContainerEndFunc(tr_variant const* /*val*/, void* vevbuf)
static void saveContainerEndFunc(tr_variant const* /*val*/, void* vbuf)
{
auto* const evbuf = static_cast<evbuffer*>(vevbuf);
evbuffer_add(evbuf, "e", 1);
static_cast<Buffer*>(vbuf)->push_back('e');
}
static struct VariantWalkFuncs const walk_funcs = {
@@ -350,7 +337,9 @@ static struct VariantWalkFuncs const walk_funcs = {
saveContainerEndFunc, //
};
void tr_variantToBufBenc(tr_variant const* top, evbuffer* buf)
std::string tr_variantToStrBenc(tr_variant const* top)
{
tr_variantWalk(top, &walk_funcs, buf, true);
auto buf = libtransmission::Buffer{};
tr_variantWalk(top, &walk_funcs, &buf, true);
return buf.toString();
}

View File

@@ -32,9 +32,9 @@ struct VariantWalkFuncs
void tr_variantWalk(tr_variant const* top, VariantWalkFuncs const* walk_funcs, void* user_data, bool sort_dicts);
void tr_variantToBufJson(tr_variant const* top, struct evbuffer* buf, bool lean);
std::string tr_variantToStrJson(tr_variant const* top, bool lean);
void tr_variantToBufBenc(tr_variant const* top, struct evbuffer* buf);
std::string tr_variantToStrBenc(tr_variant const* top);
void tr_variantInit(tr_variant* v, char type);

View File

@@ -10,14 +10,13 @@
#include <cstdlib>
#include <cstring>
#include <deque>
#include <string>
#include <string_view>
#include <utility>
#define UTF_CPP_CPLUSPLUS 201703L
#include <utf8.h>
#include <event2/buffer.h>
#include <fmt/compile.h>
#include <fmt/format.h>
@@ -30,12 +29,15 @@
#include "log.h"
#include "quark.h"
#include "tr-assert.h"
#include "tr-buffer.h"
#include "utils.h"
#include "variant-common.h"
#include "variant.h"
using namespace std::literals;
using Buffer = libtransmission::Buffer;
/* arbitrary value... this is much deeper than our code goes */
static auto constexpr MaxDepth = int{ 64 };
@@ -44,8 +46,8 @@ struct json_wrapper_data
bool has_content;
size_t size;
std::string_view key;
evbuffer* keybuf;
evbuffer* strbuf;
std::string keybuf;
std::string strbuf;
tr_error* error;
std::deque<tr_variant*> stack;
tr_variant* top;
@@ -168,11 +170,11 @@ static bool decode_hex_string(char const* in, unsigned int* setme)
return true;
}
static std::string_view extract_escaped_string(char const* in, size_t in_len, evbuffer* buf)
static std::string_view extract_escaped_string(char const* in, size_t in_len, std::string& buf)
{
char const* const in_end = in + in_len;
evbuffer_drain(buf, evbuffer_get_length(buf));
buf.clear();
while (in < in_end)
{
@@ -183,49 +185,49 @@ static std::string_view extract_escaped_string(char const* in, size_t in_len, ev
switch (in[1])
{
case 'b':
evbuffer_add(buf, "\b", 1);
buf.push_back('\b');
in += 2;
unescaped = true;
break;
case 'f':
evbuffer_add(buf, "\f", 1);
buf.push_back('\f');
in += 2;
unescaped = true;
break;
case 'n':
evbuffer_add(buf, "\n", 1);
buf.push_back('\n');
in += 2;
unescaped = true;
break;
case 'r':
evbuffer_add(buf, "\r", 1);
buf.push_back('\r');
in += 2;
unescaped = true;
break;
case 't':
evbuffer_add(buf, "\t", 1);
buf.push_back('\t');
in += 2;
unescaped = true;
break;
case '/':
evbuffer_add(buf, "/", 1);
buf.push_back('/');
in += 2;
unescaped = true;
break;
case '"':
evbuffer_add(buf, "\"", 1);
buf.push_back('"');
in += 2;
unescaped = true;
break;
case '\\':
evbuffer_add(buf, "\\", 1);
buf.push_back('\\');
in += 2;
unescaped = true;
break;
@@ -241,11 +243,11 @@ static std::string_view extract_escaped_string(char const* in, size_t in_len, ev
{
auto buf8 = std::array<char, 8>{};
auto const it = utf8::append(val, std::data(buf8));
evbuffer_add(buf, std::data(buf8), it - std::data(buf8));
buf.append(std::data(buf8), it - std::data(buf8));
}
catch (utf8::exception const&)
{ // invalid codepoint
evbuffer_add(buf, "?", 1);
catch (utf8::exception const&) // invalid codepoint
{
buf.push_back('?');
}
unescaped = true;
in += 6;
@@ -257,15 +259,15 @@ static std::string_view extract_escaped_string(char const* in, size_t in_len, ev
if (!unescaped)
{
evbuffer_add(buf, in, 1);
buf.push_back(*in);
++in;
}
}
return { (char const*)evbuffer_pullup(buf, -1), evbuffer_get_length(buf) };
return buf;
}
static std::pair<std::string_view, bool> extract_string(jsonsl_t jsn, struct jsonsl_state_st* state, evbuffer* buf)
static std::pair<std::string_view, bool> extract_string(jsonsl_t jsn, struct jsonsl_state_st* state, std::string& buf)
{
// figure out where the string is
char const* in_begin = jsn->base + state->pos_begin;
@@ -367,11 +369,9 @@ bool tr_variantParseJson(tr_variant& setme, int parse_opts, std::string_view jso
data.size = std::size(json);
data.has_content = false;
data.key = ""sv;
data.keybuf = evbuffer_new();
data.parse_opts = parse_opts;
data.preallocGuess = {};
data.stack = {};
data.strbuf = evbuffer_new();
data.top = &setme;
/* parse it */
@@ -395,8 +395,6 @@ bool tr_variantParseJson(tr_variant& setme, int parse_opts, std::string_view jso
{
tr_error_propagate(error, &data.error);
}
evbuffer_free(data.keybuf);
evbuffer_free(data.strbuf);
jsonsl_destroy(jsn);
return success;
}
@@ -412,14 +410,19 @@ struct ParentState
int childCount;
};
struct jsonWalk
struct JsonWalk
{
bool doIndent = false;
JsonWalk(bool do_indent)
: doIndent{ do_indent }
{
}
std::deque<ParentState> parents;
evbuffer* out = nullptr;
Buffer out;
bool doIndent;
};
static void jsonIndent(struct jsonWalk* data)
static void jsonIndent(struct JsonWalk* data)
{
static auto buf = std::array<char, 1024>{};
@@ -431,11 +434,11 @@ static void jsonIndent(struct jsonWalk* data)
if (data->doIndent)
{
evbuffer_add(data->out, std::data(buf), std::size(data->parents) * 4 + 1);
data->out.add(std::data(buf), std::size(data->parents) * 4 + 1);
}
}
static void jsonChildFunc(struct jsonWalk* data)
static void jsonChildFunc(struct JsonWalk* data)
{
if (!std::empty(data->parents))
{
@@ -450,14 +453,14 @@ static void jsonChildFunc(struct jsonWalk* data)
if (i % 2 == 0)
{
evbuffer_add(data->out, ": ", data->doIndent ? 2 : 1);
data->out.add(data->doIndent ? ": "sv : ":"sv);
}
else
{
bool const is_last = pstate.childIndex == pstate.childCount;
if (!is_last)
{
evbuffer_add(data->out, ",", 1);
data->out.push_back(',');
jsonIndent(data);
}
}
@@ -469,7 +472,7 @@ static void jsonChildFunc(struct jsonWalk* data)
++pstate.childIndex;
if (bool const is_last = pstate.childIndex == pstate.childCount; !is_last)
{
evbuffer_add(data->out, ",", 1);
data->out.push_back(',');
jsonIndent(data);
}
@@ -481,13 +484,13 @@ static void jsonChildFunc(struct jsonWalk* data)
}
}
static void jsonPushParent(struct jsonWalk* data, tr_variant const* v)
static void jsonPushParent(struct JsonWalk* data, tr_variant const* v)
{
int const n_children = tr_variantIsDict(v) ? v->val.l.count * 2 : v->val.l.count;
data->parents.push_back({ v->type, 0, n_children });
}
static void jsonPopParent(struct jsonWalk* data)
static void jsonPopParent(struct JsonWalk* data)
{
data->parents.pop_back();
}
@@ -496,22 +499,22 @@ static void jsonIntFunc(tr_variant const* val, void* vdata)
{
auto buf = std::array<char, 64>{};
auto const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:d}"), val->val.i);
auto* const data = static_cast<jsonWalk*>(vdata);
evbuffer_add(data->out, std::data(buf), static_cast<size_t>(out - std::data(buf)));
auto* const data = static_cast<JsonWalk*>(vdata);
data->out.add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
jsonChildFunc(data);
}
static void jsonBoolFunc(tr_variant const* val, void* vdata)
{
auto* data = static_cast<struct jsonWalk*>(vdata);
auto* data = static_cast<struct JsonWalk*>(vdata);
if (val->val.b)
{
evbuffer_add(data->out, "true", 4);
data->out.add("true"sv);
}
else
{
evbuffer_add(data->out, "false", 5);
data->out.add("false"sv);
}
jsonChildFunc(data);
@@ -519,19 +522,19 @@ static void jsonBoolFunc(tr_variant const* val, void* vdata)
static void jsonRealFunc(tr_variant const* val, void* vdata)
{
auto* data = static_cast<struct jsonWalk*>(vdata);
auto* data = static_cast<struct JsonWalk*>(vdata);
if (fabs(val->val.d - (int)val->val.d) < 0.00001)
{
auto buf = std::array<char, 64>{};
auto const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:.0f}"), val->val.d);
evbuffer_add(data->out, std::data(buf), static_cast<size_t>(out - std::data(buf)));
data->out.add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
}
else
{
auto buf = std::array<char, 64>{};
auto const out = fmt::format_to(std::data(buf), FMT_COMPILE("{:.4f}"), val->val.d);
evbuffer_add(data->out, std::data(buf), static_cast<size_t>(out - std::data(buf)));
data->out.add(std::data(buf), static_cast<size_t>(out - std::data(buf)));
}
jsonChildFunc(data);
@@ -539,96 +542,85 @@ static void jsonRealFunc(tr_variant const* val, void* vdata)
static void jsonStringFunc(tr_variant const* val, void* vdata)
{
evbuffer_iovec vec[1];
auto* data = static_cast<struct jsonWalk*>(vdata);
auto* data = static_cast<struct JsonWalk*>(vdata);
auto sv = std::string_view{};
(void)!tr_variantGetStrView(val, &sv);
evbuffer_reserve_space(data->out, std::size(sv) * 6 + 2, vec, 1);
auto* out = static_cast<char*>(vec[0].iov_base);
char const* const outend = out + vec[0].iov_len;
char* outwalk = out;
*outwalk++ = '"';
auto& out = data->out;
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':
*outwalk++ = '\\';
*outwalk++ = 'b';
out.add(R"(\b)"sv);
break;
case '\f':
*outwalk++ = '\\';
*outwalk++ = 'f';
out.add(R"(\f)"sv);
break;
case '\n':
*outwalk++ = '\\';
*outwalk++ = 'n';
out.add(R"(\n)"sv);
break;
case '\r':
*outwalk++ = '\\';
*outwalk++ = 'r';
out.add(R"(\r)"sv);
break;
case '\t':
*outwalk++ = '\\';
*outwalk++ = 't';
out.add(R"(\t)"sv);
break;
case '"':
*outwalk++ = '\\';
*outwalk++ = '"';
out.add(R"(\")"sv);
break;
case '\\':
*outwalk++ = '\\';
*outwalk++ = '\\';
out.add(R"(\\)"sv);
break;
default:
if (isprint((unsigned char)sv.front()) != 0)
{
*outwalk++ = sv.front();
out.push_back(sv.front());
}
else
{
try
{
auto arr = std::array<char, 16>{};
auto const* const begin8 = std::data(sv);
auto const* const end8 = begin8 + std::size(sv);
auto const* walk8 = begin8;
auto const uch32 = utf8::next(walk8, end8);
outwalk = fmt::format_to_n(outwalk, outend - outwalk - 1, FMT_COMPILE("\\u{:04x}"), uch32).out;
auto const result = fmt::format_to_n(std::data(arr), std::size(arr), FMT_COMPILE("\\u{:04x}"), uch32);
out.add(std::data(arr), result.size);
sv.remove_prefix(walk8 - begin8 - 1);
}
catch (utf8::exception const&)
{
*outwalk++ = '?';
out.push_back('?');
}
}
break;
}
}
*outwalk++ = '"';
vec[0].iov_len = outwalk - out;
evbuffer_commit_space(data->out, vec, 1);
out.push_back('"');
jsonChildFunc(data);
}
static void jsonDictBeginFunc(tr_variant const* val, void* vdata)
{
auto* data = static_cast<struct jsonWalk*>(vdata);
auto* data = static_cast<struct JsonWalk*>(vdata);
jsonPushParent(data, val);
evbuffer_add(data->out, "{", 1);
data->out.push_back('{');
if (val->val.l.count != 0)
{
@@ -639,10 +631,10 @@ static void jsonDictBeginFunc(tr_variant const* val, void* vdata)
static void jsonListBeginFunc(tr_variant const* val, void* vdata)
{
size_t const n_children = tr_variantListSize(val);
auto* data = static_cast<struct jsonWalk*>(vdata);
auto* data = static_cast<struct JsonWalk*>(vdata);
jsonPushParent(data, val);
evbuffer_add(data->out, "[", 1);
data->out.push_back('[');
if (n_children != 0)
{
@@ -652,7 +644,7 @@ static void jsonListBeginFunc(tr_variant const* val, void* vdata)
static void jsonContainerEndFunc(tr_variant const* val, void* vdata)
{
auto* data = static_cast<struct jsonWalk*>(vdata);
auto* data = static_cast<struct JsonWalk*>(vdata);
jsonPopParent(data);
@@ -660,11 +652,11 @@ static void jsonContainerEndFunc(tr_variant const* val, void* vdata)
if (tr_variantIsDict(val))
{
evbuffer_add(data->out, "}", 1);
data->out.push_back('}');
}
else /* list */
{
evbuffer_add(data->out, "]", 1);
data->out.push_back(']');
}
jsonChildFunc(data);
@@ -680,17 +672,16 @@ static struct VariantWalkFuncs const walk_funcs = {
jsonContainerEndFunc, //
};
void tr_variantToBufJson(tr_variant const* top, evbuffer* buf, bool lean)
std::string tr_variantToStrJson(tr_variant const* top, bool lean)
{
struct jsonWalk data;
data.doIndent = !lean;
data.out = buf;
auto data = JsonWalk{ !lean };
tr_variantWalk(top, &walk_funcs, &data, true);
if (evbuffer_get_length(buf) != 0)
auto& buf = data.out;
if (!std::empty(buf))
{
evbuffer_add_printf(buf, "\n");
buf.push_back('\n');
}
return buf.toString();
}

View File

@@ -15,8 +15,6 @@
#include <share.h>
#endif
#include <event2/buffer.h>
#include <fmt/core.h>
#define LIBTRANSMISSION_VARIANT_MODULE
@@ -1098,39 +1096,22 @@ void tr_variantMergeDicts(tr_variant* target, tr_variant const* source)
****
***/
struct evbuffer* tr_variantToBuf(tr_variant const* v, tr_variant_fmt fmt)
std::string tr_variantToStr(tr_variant const* v, tr_variant_fmt fmt)
{
struct evbuffer* buf = evbuffer_new();
evbuffer_expand(buf, 4096); /* alloc a little memory to start off with */
switch (fmt)
{
case TR_VARIANT_FMT_BENC:
tr_variantToBufBenc(v, buf);
break;
case TR_VARIANT_FMT_JSON:
tr_variantToBufJson(v, buf, false);
return tr_variantToStrJson(v, false);
break;
case TR_VARIANT_FMT_JSON_LEAN:
tr_variantToBufJson(v, buf, true);
return tr_variantToStrJson(v, true);
break;
default: // TR_VARIANT_FMT_BENC:
return tr_variantToStrBenc(v);
break;
}
return buf;
}
std::string tr_variantToStr(tr_variant const* v, tr_variant_fmt fmt)
{
auto* const buf = tr_variantToBuf(v, fmt);
auto const n = evbuffer_get_length(buf);
auto str = std::string{};
str.resize(n);
evbuffer_copyout(buf, std::data(str), n);
evbuffer_free(buf);
return str;
}
int tr_variantToFile(tr_variant const* v, tr_variant_fmt fmt, std::string_view filename)

View File

@@ -12,8 +12,6 @@
#include "quark.h"
struct evbuffer;
struct tr_error;
/**
@@ -114,9 +112,7 @@ enum tr_variant_fmt
int tr_variantToFile(tr_variant const* variant, tr_variant_fmt fmt, std::string_view filename);
std::string tr_variantToStr(tr_variant const* variant, tr_variant_fmt fmt);
struct evbuffer* tr_variantToBuf(tr_variant const* variant, tr_variant_fmt fmt);
[[nodiscard]] std::string tr_variantToStr(tr_variant const* variant, tr_variant_fmt fmt);
enum tr_variant_parse_opts
{

View File

@@ -31,7 +31,6 @@ Q_DECLARE_METATYPE(TrVariantPtr)
extern "C"
{
struct evbuffer;
struct tr_session;
}

View File

@@ -5,6 +5,7 @@ add_executable(libtransmission-test
bitfield-test.cc
block-info-test.cc
blocklist-test.cc
buffer-test.cc
clients-test.cc
completion-test.cc
copy-test.cc

View File

@@ -0,0 +1,63 @@
// This file Copyright (C) 2013-2022 Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include "transmission.h"
#include "tr-buffer.h"
#include "test-fixtures.h"
using BufferTest = ::testing::Test;
using namespace std::literals;
using Buffer = libtransmission::Buffer;
TEST_F(BufferTest, startsWithInSingleSegment)
{
auto constexpr Hello = "Hello, "sv;
auto constexpr World = "World"sv;
auto constexpr Bang = "!"sv;
auto buf = Buffer{};
buf.add(Hello);
EXPECT_TRUE(buf.startsWith(Hello));
buf.add(World);
EXPECT_TRUE(buf.startsWith(Hello));
EXPECT_TRUE(buf.startsWith("Hello, Worl"sv));
EXPECT_TRUE(buf.startsWith("Hello, World"sv));
EXPECT_FALSE(buf.startsWith("Hello, World!"sv));
EXPECT_FALSE(buf.startsWith("Hello!"sv));
buf.add(Bang);
EXPECT_FALSE(buf.startsWith("Hello!"));
EXPECT_TRUE(buf.startsWith(Hello));
EXPECT_TRUE(buf.startsWith("Hello, Worl"sv));
EXPECT_TRUE(buf.startsWith("Hello, World"sv));
EXPECT_TRUE(buf.startsWith("Hello, World!"sv));
}
TEST_F(BufferTest, startsWithInMultiSegment)
{
auto constexpr Hello = "Hello, "sv;
auto constexpr World = "World"sv;
auto constexpr Bang = "!"sv;
auto buf = std::make_unique<Buffer>();
buf->add(Buffer{ Hello });
EXPECT_TRUE(buf->startsWith(Hello));
buf->add(Buffer{ World });
EXPECT_TRUE(buf->startsWith(Hello));
EXPECT_TRUE(buf->startsWith("Hello, Worl"sv));
EXPECT_TRUE(buf->startsWith("Hello, World"sv));
EXPECT_FALSE(buf->startsWith("Hello, World!"sv));
EXPECT_FALSE(buf->startsWith("Hello!"sv));
buf->add(Buffer{ Bang });
EXPECT_FALSE(buf->startsWith("Hello!"));
EXPECT_TRUE(buf->startsWith(Hello));
EXPECT_TRUE(buf->startsWith("Hello, Worl"sv));
EXPECT_TRUE(buf->startsWith("Hello, World"sv));
EXPECT_TRUE(buf->startsWith("Hello, World!"sv));
}