mirror of
https://github.com/transmission/transmission.git
synced 2025-12-20 02:18:42 +00:00
refactor: extract tr_buffer class from tr_peerIo (#3986)
This commit is contained in:
@@ -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 */,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)));
|
||||
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;
|
||||
encrypt(1, &*iter);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,18 +946,17 @@ 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)
|
||||
{
|
||||
if (error->code != EAGAIN && error->code != EINTR && error->code != EINPROGRESS && io->gotError != nullptr)
|
||||
{
|
||||
short what = BEV_EVENT_READING | BEV_EVENT_ERROR;
|
||||
|
||||
@@ -1002,11 +967,19 @@ static int tr_peerIoTryRead(tr_peerIo* io, size_t howmuch)
|
||||
|
||||
tr_logAddTraceIo(
|
||||
io,
|
||||
fmt::format("tr_peerIoTryRead err: res:{} what:{}, errno:{} ({})", res, what, e, tr_net_strerror(e)));
|
||||
fmt::format(
|
||||
"tr_peerIoTryRead err: res:{} what:{}, errno:{} ({})",
|
||||
res,
|
||||
what,
|
||||
error->code,
|
||||
error->message));
|
||||
|
||||
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,15 +1006,16 @@ 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);
|
||||
|
||||
{
|
||||
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)
|
||||
{
|
||||
evbuffer_drain(io->outbuf.get(), n);
|
||||
io->outbuf.drain(n);
|
||||
didWriteWrapper(io, n);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TR_PEER_SOCKET_TYPE_TCP:
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
335
libtransmission/tr-buffer.h
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -31,7 +31,6 @@ Q_DECLARE_METATYPE(TrVariantPtr)
|
||||
|
||||
extern "C"
|
||||
{
|
||||
struct evbuffer;
|
||||
struct tr_session;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
63
tests/libtransmission/buffer-test.cc
Normal file
63
tests/libtransmission/buffer-test.cc
Normal 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));
|
||||
}
|
||||
Reference in New Issue
Block a user