refactor: use new tr_variant API in peer-msgs.cc (#8091)

* refactor: convert `tr_peerMsgsImpl::parse_ut_pex()`

* refactor: convert `tr_peerMsgsImpl::send_ltep_handshake()`

* refactor: convert `tr_peerMsgsImpl::parse_ltep_handshake()`

* refactor: convert `tr_peerMsgsImpl::parse_ut_metadata()`

* refactor: convert `tr_peerMsgsImpl::maybe_send_metadata_requests()`

* refactor: convert `tr_peerMsgsImpl::add_next_metadata_piece()`
This commit is contained in:
Yat Ho
2026-01-10 05:58:20 +08:00
committed by GitHub
parent 47832db7b2
commit f2d2edbdb9
2 changed files with 160 additions and 138 deletions

View File

@@ -1470,6 +1470,7 @@ void tr_peerMgrAddIncoming(tr_peerMgr* manager, tr_peer_socket&& socket)
}
}
// TODO(C++20): convert to std::span
size_t tr_peerMgrAddPex(tr_torrent* tor, tr_peer_from from, tr_pex const* pex, size_t n_pex)
{
size_t n_used = 0;
@@ -1490,6 +1491,7 @@ size_t tr_peerMgrAddPex(tr_torrent* tor, tr_peer_from from, tr_pex const* pex, s
return n_used;
}
// TODO(C++20): convert to std::span
std::vector<tr_pex> tr_pex::from_compact_ipv4(
void const* compact,
size_t compact_len,
@@ -1513,6 +1515,7 @@ std::vector<tr_pex> tr_pex::from_compact_ipv4(
return pex;
}
// TODO(C++20): convert to std::span
std::vector<tr_pex> tr_pex::from_compact_ipv6(
void const* compact,
size_t compact_len,

View File

@@ -984,41 +984,40 @@ void tr_peerMsgsImpl::parse_ut_pex(MessageReader& payload)
return;
}
if (auto var = tr_variant_serde::benc().inplace().parse(payload.to_string_view()); var)
auto const var = tr_variant_serde::benc().inplace().parse(payload.to_string_view());
if (!var)
{
logtrace(this, "got ut pex");
return;
}
uint8_t const* added = nullptr;
auto added_len = size_t{};
if (tr_variantDictFindRaw(&*var, TR_KEY_added, &added, &added_len))
{
uint8_t const* added_f = nullptr;
auto added_f_len = size_t{};
if (!tr_variantDictFindRaw(&*var, TR_KEY_added_f, &added_f, &added_f_len))
{
added_f_len = 0;
added_f = nullptr;
}
auto const* const map = var->get_if<tr_variant::Map>();
if (map == nullptr)
{
return;
}
auto pex = tr_pex::from_compact_ipv4(added, added_len, added_f, added_f_len);
pex.resize(std::min(MaxPexPeerCount, std::size(pex)));
tr_peerMgrAddPex(&tor_, TR_PEER_FROM_PEX, std::data(pex), std::size(pex));
}
logtrace(this, "got ut pex");
if (tr_variantDictFindRaw(&*var, TR_KEY_added6, &added, &added_len))
{
uint8_t const* added_f = nullptr;
auto added_f_len = size_t{};
if (!tr_variantDictFindRaw(&*var, TR_KEY_added6_f, &added_f, &added_f_len))
{
added_f_len = 0;
added_f = nullptr;
}
if (auto const added = map->value_if<std::string_view>(TR_KEY_added))
{
auto const added_f_sv = map->value_if<std::string_view>(TR_KEY_added_f);
auto const* const added_f = added_f_sv ? reinterpret_cast<uint8_t const*>(std::data(*added_f_sv)) : nullptr;
auto const added_f_len = added_f_sv ? std::size(*added_f_sv) : 0U;
auto pex = tr_pex::from_compact_ipv6(added, added_len, added_f, added_f_len);
pex.resize(std::min(MaxPexPeerCount, std::size(pex)));
tr_peerMgrAddPex(&tor_, TR_PEER_FROM_PEX, std::data(pex), std::size(pex));
}
auto pex = tr_pex::from_compact_ipv4(std::data(*added), std::size(*added), added_f, added_f_len);
pex.resize(std::min(MaxPexPeerCount, std::size(pex)));
tr_peerMgrAddPex(&tor_, TR_PEER_FROM_PEX, std::data(pex), std::size(pex));
}
if (auto const added = map->value_if<std::string_view>(TR_KEY_added6))
{
auto const added_f_sv = map->value_if<std::string_view>(TR_KEY_added6_f);
auto const* const added_f = added_f_sv ? reinterpret_cast<uint8_t const*>(std::data(*added_f_sv)) : nullptr;
auto const added_f_len = added_f_sv ? std::size(*added_f_sv) : 0U;
auto pex = tr_pex::from_compact_ipv6(std::data(*added), std::size(*added), added_f, added_f_len);
pex.resize(std::min(MaxPexPeerCount, std::size(pex)));
tr_peerMgrAddPex(&tor_, TR_PEER_FROM_PEX, std::data(pex), std::size(pex));
}
}
@@ -1139,9 +1138,8 @@ void tr_peerMsgsImpl::send_ltep_handshake()
/* decide if we want to advertise pex support */
bool const allow_pex = tor_.allows_pex();
auto val = tr_variant{};
tr_variantInitDict(&val, 8);
tr_variantDictAddBool(&val, TR_KEY_e, session->encryptionMode() != TR_CLEAR_PREFERRED);
auto val = tr_variant::Map{ 8U };
val.try_emplace(TR_KEY_e, session->encryptionMode() != TR_CLEAR_PREFERRED);
// If connecting to global peer, then use global address
// Otherwise we are connecting to local peer, use bind address directly
@@ -1150,14 +1148,18 @@ void tr_peerMsgsImpl::send_ltep_handshake()
addr && !addr->is_any())
{
TR_ASSERT(addr->is_ipv4());
tr_variantDictAddRaw(&val, TR_KEY_ipv4, &addr->addr.addr4, sizeof(addr->addr.addr4));
val.try_emplace(
TR_KEY_ipv4,
std::string_view{ reinterpret_cast<char const*>(&addr->addr.addr4), sizeof(addr->addr.addr4) });
}
if (auto const addr = io_->address().is_global_unicast() ? session->global_address(TR_AF_INET6) :
session->bind_address(TR_AF_INET6);
addr && !addr->is_any())
{
TR_ASSERT(addr->is_ipv6());
tr_variantDictAddRaw(&val, TR_KEY_ipv6, &addr->addr.addr6, sizeof(addr->addr.addr6));
val.try_emplace(
TR_KEY_ipv6,
std::string_view{ reinterpret_cast<char const*>(&addr->addr.addr6), sizeof(addr->addr.addr6) });
}
// https://www.bittorrent.org/beps/bep_0009.html
@@ -1166,7 +1168,7 @@ void tr_peerMsgsImpl::send_ltep_handshake()
// bytes of the metadata.
if (auto const info_dict_size = tor_.info_dict_size(); allow_metadata_xfer && tor_.has_metainfo() && info_dict_size > 0)
{
tr_variantDictAddInt(&val, TR_KEY_metadata_size, info_dict_size);
val.try_emplace(TR_KEY_metadata_size, info_dict_size);
}
// https://www.bittorrent.org/beps/bep_0010.html
@@ -1174,12 +1176,12 @@ void tr_peerMsgsImpl::send_ltep_handshake()
// port number of the other side. Note that there is no need for the
// receiving side of the connection to send this extension message,
// since its port number is already known.
tr_variantDictAddInt(&val, TR_KEY_p, session->advertisedPeerPort().host());
val.try_emplace(TR_KEY_p, session->advertisedPeerPort().host());
// https://www.bittorrent.org/beps/bep_0010.html
// An integer, the number of outstanding request messages this
// client supports without dropping any.
tr_variantDictAddInt(&val, TR_KEY_reqq, client_reqq());
val.try_emplace(TR_KEY_reqq, client_reqq());
// https://www.bittorrent.org/beps/bep_0010.html
// A string containing the compact representation of the ip address this peer sees
@@ -1189,40 +1191,42 @@ void tr_peerMsgsImpl::send_ltep_handshake()
auto buf = std::array<std::byte, TrAddrStrlen>{};
auto const begin = std::data(buf);
auto const end = io_->address().to_compact(begin);
auto const len = end - begin;
auto const len = static_cast<size_t>(end - begin);
TR_ASSERT(len == tr_address::CompactAddrBytes[0] || len == tr_address::CompactAddrBytes[1]);
tr_variantDictAddRaw(&val, TR_KEY_yourip, begin, len);
val.try_emplace(TR_KEY_yourip, std::string_view{ reinterpret_cast<char*>(begin), len });
}
// https://www.bittorrent.org/beps/bep_0010.html
// Client name and version (as a utf-8 string). This is a much more
// reliable way of identifying the client than relying on the
// peer id encoding.
tr_variantDictAddStrView(&val, TR_KEY_v, TR_NAME " " USERAGENT_PREFIX);
val.try_emplace(TR_KEY_v, tr_variant::unmanaged_string(TR_NAME " " USERAGENT_PREFIX));
// https://www.bittorrent.org/beps/bep_0021.html
// A peer that is a partial seed SHOULD include an extra header in
// the extension handshake 'upload_only'. Setting the value of this
// key to 1 indicates that this peer is not interested in downloading
// anything.
tr_variantDictAddBool(&val, TR_KEY_upload_only, tor_.is_done());
val.try_emplace(TR_KEY_upload_only, tor_.is_done());
if (allow_metadata_xfer || allow_pex)
{
tr_variant* m = tr_variantDictAddDict(&val, TR_KEY_m, 2);
auto m = tr_variant::Map{ 2U };
if (allow_metadata_xfer)
{
tr_variantDictAddInt(m, TR_KEY_ut_metadata, UT_METADATA_ID);
m.try_emplace(TR_KEY_ut_metadata, UT_METADATA_ID);
}
if (allow_pex)
{
tr_variantDictAddInt(m, TR_KEY_ut_pex, UT_PEX_ID);
m.try_emplace(TR_KEY_ut_pex, UT_PEX_ID);
}
val.try_emplace(TR_KEY_m, std::move(m));
}
protocol_send_message(BtPeerMsgs::Ltep, LtepMessages::Handshake, tr_variant_serde::benc().to_string(val));
protocol_send_message(BtPeerMsgs::Ltep, LtepMessages::Handshake, tr_variant_serde::benc().to_string(std::move(val)));
}
void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
@@ -1243,34 +1247,37 @@ void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
logwarn(this, "got ltep handshake, but peer did not advertise support in reserved bytes");
}
auto const* const map = var->get_if<tr_variant::Map>();
TR_ASSERT(map != nullptr);
// does the peer prefer encrypted connections?
if (auto e = int64_t{}; tr_variantDictFindInt(&*var, TR_KEY_e, &e))
if (auto const e = map->value_if<int64_t>(TR_KEY_e))
{
peer_info->set_encryption_preferred(e != 0);
peer_info->set_encryption_preferred(*e != 0);
}
// check supported messages for utorrent pex
auto holepunch_supported = false;
if (tr_variant* sub = nullptr; tr_variantDictFindDict(&*var, TR_KEY_m, &sub))
if (auto const* const sub = map->find_if<tr_variant::Map>(TR_KEY_m))
{
auto const tor_is_public = tor_.is_public();
if (auto ut_pex = int64_t{}; tor_is_public && tr_variantDictFindInt(sub, TR_KEY_ut_pex, &ut_pex))
if (tor_.is_public())
{
ut_pex_id_ = static_cast<uint8_t>(ut_pex);
logtrace(this, fmt::format("msgs->ut_pex is {:d}", ut_pex_id_));
if (auto const ut_pex = sub->value_if<int64_t>(TR_KEY_ut_pex))
{
ut_pex_id_ = static_cast<uint8_t>(*ut_pex);
logtrace(this, fmt::format("msgs->ut_pex is {:d}", ut_pex_id_));
}
if (auto const ut_metadata = sub->value_if<int64_t>(TR_KEY_ut_metadata))
{
ut_metadata_id_ = static_cast<uint8_t>(*ut_metadata);
logtrace(this, fmt::format("msgs->ut_metadata_id_ is {:d}", ut_metadata_id_));
}
}
if (auto ut_metadata = int64_t{}; tor_is_public && tr_variantDictFindInt(sub, TR_KEY_ut_metadata, &ut_metadata))
if (auto const ut_holepunch = sub->value_if<int64_t>(TR_KEY_ut_holepunch))
{
ut_metadata_id_ = static_cast<uint8_t>(ut_metadata);
logtrace(this, fmt::format("msgs->ut_metadata_id_ is {:d}", ut_metadata_id_));
}
if (auto ut_holepunch = int64_t{}; tr_variantDictFindInt(sub, TR_KEY_ut_holepunch, &ut_holepunch))
{
holepunch_supported = ut_holepunch != 0;
holepunch_supported = *ut_holepunch != 0;
}
}
@@ -1286,32 +1293,34 @@ void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
peer_info->set_holepunch_supported(holepunch_supported);
// look for metainfo size (BEP 9)
if (auto metadata_size = int64_t{};
can_xfer_metadata() && tr_variantDictFindInt(&*var, TR_KEY_metadata_size, &metadata_size))
if (can_xfer_metadata())
{
if (!tr_metadata_download::is_valid_metadata_size(metadata_size))
if (auto const metadata_size = map->value_if<int64_t>(TR_KEY_metadata_size))
{
ut_metadata_id_ = 0U;
}
else
{
tor_.maybe_start_metadata_transfer(metadata_size);
if (!tr_metadata_download::is_valid_metadata_size(*metadata_size))
{
ut_metadata_id_ = 0U;
}
else
{
tor_.maybe_start_metadata_transfer(*metadata_size);
}
}
}
// look for upload_only (BEP 21)
if (auto upload_only = int64_t{}; tr_variantDictFindInt(&*var, TR_KEY_upload_only, &upload_only))
if (auto const upload_only = map->value_if<int64_t>(TR_KEY_upload_only))
{
peer_info->set_upload_only(upload_only != 0);
peer_info->set_upload_only(*upload_only != 0);
}
// https://www.bittorrent.org/beps/bep_0010.html
// Client name and version (as a utf-8 string). This is a much more
// reliable way of identifying the client than relying on the
// peer id encoding.
if (auto sv = std::string_view{}; tr_variantDictFindStrView(&*var, TR_KEY_v, &sv))
if (auto const sv = map->value_if<std::string_view>(TR_KEY_v))
{
set_user_agent(tr_interned_string{ tr_strv_convert_utf8(sv) });
set_user_agent(tr_interned_string{ tr_strv_convert_utf8(*sv) });
}
// https://www.bittorrent.org/beps/bep_0010.html
@@ -1319,10 +1328,10 @@ void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
// this peer sees you as. i.e. this is the receiver's external ip
// address (no port is included). This may be either an IPv4 (4 bytes)
// or an IPv6 (16 bytes) address.
if (auto sv = std::string_view{}; tr_variantDictFindStrView(&*var, TR_KEY_yourip, &sv))
if (auto const sv = map->value_if<std::string_view>(TR_KEY_yourip))
{
auto const* const bytes = reinterpret_cast<std::byte const*>(std::data(sv));
switch (std::size(sv))
auto const* const bytes = reinterpret_cast<std::byte const*>(std::data(*sv));
switch (std::size(*sv))
{
case tr_address::CompactAddrBytes[TR_AF_INET]:
peer_info->maybe_update_canonical_priority(tr_address::from_compact_ipv4(bytes).first);
@@ -1336,32 +1345,35 @@ void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
}
/* get peer's listening port */
if (auto p = int64_t{}; tr_variantDictFindInt(&*var, TR_KEY_p, &p) && p > 0)
if (auto const p = map->value_if<int64_t>(TR_KEY_p).value_or(0); p > 0)
{
publish(tr_peer_event::GotPort(tr_port::from_host(p)));
logtrace(this, fmt::format("peer's port is now {:d}", p));
}
std::byte const* addr_compact = nullptr;
auto addr_len = size_t{};
if (io_->is_incoming() && tr_variantDictFindRaw(&*var, TR_KEY_ipv4, &addr_compact, &addr_len) &&
addr_len == tr_address::CompactAddrBytes[TR_AF_INET])
if (io_->is_incoming())
{
auto pex = tr_pex{ peer_info->listen_socket_address(), peer_info->pex_flags() };
pex.socket_address.address_ = tr_address::from_compact_ipv4(addr_compact).first;
tr_peerMgrAddPex(&tor_, TR_PEER_FROM_LTEP, &pex, 1);
}
if (auto const addr_compact = map->value_if<std::string_view>(TR_KEY_ipv4);
addr_compact && std::size(*addr_compact) == tr_address::CompactAddrBytes[TR_AF_INET])
{
auto pex = tr_pex{ peer_info->listen_socket_address(), peer_info->pex_flags() };
auto const* const bytes = reinterpret_cast<std::byte const*>(std::data(*addr_compact));
pex.socket_address.address_ = tr_address::from_compact_ipv4(bytes).first;
tr_peerMgrAddPex(&tor_, TR_PEER_FROM_LTEP, &pex, 1);
}
if (io_->is_incoming() && tr_variantDictFindRaw(&*var, TR_KEY_ipv6, &addr_compact, &addr_len) &&
addr_len == tr_address::CompactAddrBytes[TR_AF_INET6])
{
auto pex = tr_pex{ peer_info->listen_socket_address(), peer_info->pex_flags() };
pex.socket_address.address_ = tr_address::from_compact_ipv6(addr_compact).first;
tr_peerMgrAddPex(&tor_, TR_PEER_FROM_LTEP, &pex, 1);
if (auto const addr_compact = map->value_if<std::string_view>(TR_KEY_ipv6);
addr_compact && std::size(*addr_compact) == tr_address::CompactAddrBytes[TR_AF_INET6])
{
auto pex = tr_pex{ peer_info->listen_socket_address(), peer_info->pex_flags() };
auto const* const bytes = reinterpret_cast<std::byte const*>(std::data(*addr_compact));
pex.socket_address.address_ = tr_address::from_compact_ipv6(bytes).first;
tr_peerMgrAddPex(&tor_, TR_PEER_FROM_LTEP, &pex, 1);
}
}
/* get peer's maximum request queue size */
if (auto reqq_in = int64_t{}; tr_variantDictFindInt(&*var, TR_KEY_reqq, &reqq_in) && reqq_in > 0)
if (auto const reqq_in = map->value_if<int64_t>(TR_KEY_reqq).value_or(0); reqq_in > 0)
{
peer_reqq_ = reqq_in;
}
@@ -1369,54 +1381,64 @@ void tr_peerMsgsImpl::parse_ltep_handshake(MessageReader& payload)
void tr_peerMsgsImpl::parse_ut_metadata(MessageReader& payload_in)
{
int64_t msg_type = -1;
int64_t piece = -1;
int64_t total_size = 0;
auto const tmp = payload_in.to_string_view();
auto const* const msg_end = std::data(tmp) + std::size(tmp);
auto serde = tr_variant_serde::benc();
if (auto var = serde.inplace().parse(tmp); var)
auto const var = serde.inplace().parse(tmp);
if (!var)
{
(void)tr_variantDictFindInt(&*var, TR_KEY_msg_type, &msg_type);
(void)tr_variantDictFindInt(&*var, TR_KEY_piece, &piece);
(void)tr_variantDictFindInt(&*var, TR_KEY_total_size, &total_size);
auto const base64 = tr_base64_encode(tmp);
logdbg(this, fmt::format("failed to parse ut_metadata msg: {}", base64));
return;
}
auto const* const map = var->get_if<tr_variant::Map>();
if (map == nullptr)
{
auto const base64 = tr_base64_encode(tmp);
logdbg(this, fmt::format("got ut_metadata msg that is not a dict: {}", base64));
return;
}
auto const msg_type = map->value_if<int64_t>(TR_KEY_msg_type).value_or(-1);
auto const piece = map->value_if<int64_t>(TR_KEY_piece).value_or(-1);
auto const total_size = map->value_if<int64_t>(TR_KEY_total_size).value_or(0);
logtrace(this, fmt::format("got ut_metadata msg: type {:d}, piece {:d}, total_size {:d}", msg_type, piece, total_size));
if (tor_.is_private())
{
logwarn(this, "got ut metadata in private torrent, rejecting");
}
if (msg_type == MetadataMsgType::Reject)
switch (msg_type)
{
// no-op
}
case MetadataMsgType::Data:
if (auto const piece_len = msg_end - serde.end(); piece * MetadataPieceSize + piece_len <= total_size)
{
tor_.set_metadata_piece(piece, serde.end(), piece_len);
}
break;
if (auto const piece_len = msg_end - serde.end();
msg_type == MetadataMsgType::Data && piece * MetadataPieceSize + piece_len <= total_size)
{
tor_.set_metadata_piece(piece, serde.end(), piece_len);
}
if (msg_type == MetadataMsgType::Request)
{
auto& reqs = peer_requested_metadata_pieces_;
if (piece >= 0 && tor_.has_metainfo() && can_xfer_metadata() && std::size(reqs) < MetadataReqQ)
case MetadataMsgType::Request:
if (auto& reqs = peer_requested_metadata_pieces_;
piece >= 0 && tor_.has_metainfo() && can_xfer_metadata() && std::size(reqs) < MetadataReqQ)
{
reqs.push(piece);
}
else
{
/* send a 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);
protocol_send_message(BtPeerMsgs::Ltep, ut_metadata_id_, serde.to_string(v));
// send a rejection message
auto v = tr_variant::Map{ 2U };
v.try_emplace(TR_KEY_msg_type, MetadataMsgType::Reject);
v.try_emplace(TR_KEY_piece, piece);
protocol_send_message(BtPeerMsgs::Ltep, ut_metadata_id_, serde.to_string(std::move(v)));
}
break;
case MetadataMsgType::Reject:
default:
break;
}
}
@@ -1889,11 +1911,10 @@ void tr_peerMsgsImpl::maybe_send_metadata_requests(time_t now) const
if (auto const piece = tor_.get_next_metadata_request(now); piece)
{
auto tmp = tr_variant{};
tr_variantInitDict(&tmp, 3);
tr_variantDictAddInt(&tmp, TR_KEY_msg_type, MetadataMsgType::Request);
tr_variantDictAddInt(&tmp, TR_KEY_piece, *piece);
protocol_send_message(BtPeerMsgs::Ltep, ut_metadata_id_, tr_variant_serde::benc().to_string(tmp));
auto tmp = tr_variant::Map{ 2U };
tmp.try_emplace(TR_KEY_msg_type, MetadataMsgType::Request);
tmp.try_emplace(TR_KEY_piece, *piece);
protocol_send_message(BtPeerMsgs::Ltep, ut_metadata_id_, tr_variant_serde::benc().to_string(std::move(tmp)));
}
}
@@ -1997,24 +2018,22 @@ void tr_peerMsgsImpl::check_request_timeout(time_t const now)
return {};
}
auto data = tor_.get_metadata_piece(*piece);
auto const data = tor_.get_metadata_piece(*piece);
if (!data)
{
// send a reject
auto tmp = tr_variant{};
tr_variantInitDict(&tmp, 2);
tr_variantDictAddInt(&tmp, TR_KEY_msg_type, MetadataMsgType::Reject);
tr_variantDictAddInt(&tmp, TR_KEY_piece, *piece);
return protocol_send_message(BtPeerMsgs::Ltep, ut_metadata_id_, tr_variant_serde::benc().to_string(tmp));
auto tmp = tr_variant::Map{ 2U };
tmp.try_emplace(TR_KEY_msg_type, MetadataMsgType::Reject);
tmp.try_emplace(TR_KEY_piece, *piece);
return protocol_send_message(BtPeerMsgs::Ltep, ut_metadata_id_, tr_variant_serde::benc().to_string(std::move(tmp)));
}
// send the metadata
auto tmp = tr_variant{};
tr_variantInitDict(&tmp, 3);
tr_variantDictAddInt(&tmp, TR_KEY_msg_type, MetadataMsgType::Data);
tr_variantDictAddInt(&tmp, TR_KEY_piece, *piece);
tr_variantDictAddInt(&tmp, TR_KEY_total_size, tor_.info_dict_size());
return protocol_send_message(BtPeerMsgs::Ltep, ut_metadata_id_, tr_variant_serde::benc().to_string(tmp), *data);
auto tmp = tr_variant::Map{ 3U };
tmp.try_emplace(TR_KEY_msg_type, MetadataMsgType::Data);
tmp.try_emplace(TR_KEY_piece, *piece);
tmp.try_emplace(TR_KEY_total_size, tor_.info_dict_size());
return protocol_send_message(BtPeerMsgs::Ltep, ut_metadata_id_, tr_variant_serde::benc().to_string(std::move(tmp)), *data);
}
[[nodiscard]] size_t tr_peerMsgsImpl::add_next_block(time_t now_sec, uint64_t now_msec)